Xcode11 iOS13下Google/Facebook登录控制器无法全屏模态展示求助
我完全明白你的困扰!更新Pods后,你重写了present方法想要让所有模态控制器全屏,但Google和Facebook的登录控制器还是顽固地以page sheet样式展示,临时把登录页设为rootVC虽然能解决,但又会导致TabBar消失,确实很头疼。下面给你几个可行的解决方案,帮你彻底搞定这个问题:
原因分析
iOS 13及以上系统默认的模态展示样式是pageSheet,而Google/Facebook的登录SDK可能在内部独立处理了present逻辑,没有走你重写的present方法,甚至可能在代码里强制设置了自己的modal样式,导致你的全局设置失效。
方案一:自定义UINavigationController(推荐)
如果你的界面是基于UINavigationController搭建的,自定义一个导航控制器并重写它的present方法,就能确保所有通过它发起的模态展示都使用全屏:
class FullScreenNavigationController: UINavigationController { override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { // 强制将所有要展示的模态控制器设为全屏 viewControllerToPresent.modalPresentationStyle = .fullScreen super.present(viewControllerToPresent, animated: flag, completion: completion) } }
接下来只需要把项目中所有的UINavigationController替换成这个自定义类:
- 代码里初始化导航控制器时,用
FullScreenNavigationController替代原类 - Storyboard里的导航控制器,在Identity Inspector中把Class改成
FullScreenNavigationController
这个方法既能保证全屏效果,又不会影响TabBar的正常显示,是最稳妥的方案。
方案二:在登录控制器中单独处理
如果你只想让第三方登录控制器全屏,不想影响其他模态页面,可以在你的LoginViewController里重写present方法:
class LoginViewController: UIViewController { override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { // 针对当前控制器发起的所有present操作强制全屏 viewControllerToPresent.modalPresentationStyle = .fullScreen super.present(viewControllerToPresent, animated: flag, completion: completion) } // 示例:Google登录调用代码 func triggerGoogleLogin() { let loginConfig = GIDConfiguration(clientID: "你的ClientID") GIDSignIn.sharedInstance.signIn(with: loginConfig, presenting: self) { user, error in // 处理登录结果 } } }
这样当你在登录页调用第三方登录时,对应的控制器就会以全屏展示,其他页面的模态样式不受影响。
方案三:全局设置默认模态样式
如果你希望整个App的所有模态控制器都默认全屏,可以在App启动时全局配置:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // iOS 13+ 全局设置模态展示样式为全屏 if #available(iOS 13.0, *) { UIViewController.appearance().modalPresentationStyle = .fullScreen } return true }
这个方法最简单粗暴,一次性解决所有模态页面的样式问题,包括第三方SDK的控制器。
方案四:Method Swizzling强制全屏(终极方案)
如果上面的方法都不生效(比如第三方SDK在内部强制修改了modal样式),可以用Method Swizzling来交换UIViewController的present方法,确保所有present操作都强制设置全屏:
extension UIViewController { static func setupFullScreenModal() { let originalSelector = #selector(present(_:animated:completion:)) let swizzledSelector = #selector(swizzledPresent(_:animated:completion:)) guard let originalMethod = class_getInstanceMethod(self, originalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return } // 交换方法实现 method_exchangeImplementations(originalMethod, swizzledMethod) } @objc private func swizzledPresent(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) { // 强制设置全屏 viewControllerToPresent.modalPresentationStyle = .fullScreen // 调用交换后的原方法 self.swizzledPresent(viewControllerToPresent, animated: flag, completion: completion) } }
然后在AppDelegate的didFinishLaunchingWithOptions里调用:
UIViewController.setupFullScreenModal()
⚠️ 注意:Method Swizzling需要谨慎使用,确保不会和其他第三方库的方法交换冲突,但在这个场景下是安全的,因为只是修改模态展示样式。
以上方案都不需要修改rootViewController,所以TabBar会在需要的页面正常显示,完美解决你的痛点!
内容的提问来源于stack exchange,提问作者udbhateja

