在AppDelegate中无法在rootViewController上弹出UIAlertViewController求助
你遇到的这个问题很常见——在didFinishLaunchingWithOptions里直接present弹窗失败,主要是因为此时window还没成为主窗口并显示,或者rootViewController的视图还没完成初始化和布局,导致present操作无法正常执行。
下面给你几个可行的解决方案:
方案1:确保window已激活后再弹窗
先把window设置为主窗口并显示,再尝试present:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 假设你已经初始化了rootViewController self.window = UIWindow(frame: UIScreen.main.bounds) self.window?.rootViewController = YourRootViewController() self.window?.makeKeyAndVisible() // 现在再弹窗 let alert: UIAlertController = UIAlertController(title: "This is title", message: "This is message.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default)) self.window?.rootViewController?.present(alert, animated: true, completion: nil) return true }
如果你的项目是用Storyboard初始化的,确保Storyboard的入口ViewController已经正确设置,并且window会自动初始化,但还是建议在弹窗前确认window?.makeKeyAndVisible()已经执行。
方案2:延迟到rootViewController的视图加载完成后
更稳妥的方式是把弹窗逻辑放到rootViewController的viewDidAppear(_:)方法里,因为此时视图已经完全显示,present操作肯定能生效:
// 在你的rootViewController类中 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let alert = UIAlertController(title: "This is title", message: "This is message.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default)) present(alert, animated: true) }
如果只需要在App首次启动时显示弹窗,可以加个标记(比如用UserDefaults记录是否已经显示过):
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let hasShownAlert = UserDefaults.standard.bool(forKey: "HasShownLaunchAlert") if !hasShownAlert { let alert = UIAlertController(title: "This is title", message: "This is message.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default) { _ in UserDefaults.standard.set(true, forKey: "HasShownLaunchAlert") }) present(alert, animated: true) } }
方案3:使用DispatchQueue延迟执行(临时方案)
如果一定要在AppDelegate里处理,可以用DispatchQueue.main.asyncAfter延迟一点时间,让window和rootVC完成初始化:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 确保window已设置 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { let alert: UIAlertController = UIAlertController(title: "This is title", message: "This is message.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default)) self.window?.rootViewController?.present(alert, animated: true, completion: nil) } return true }
不过这种方式不够可靠,因为不同设备的初始化速度不同,延迟时间不好把控,优先推荐前两种方案。
针对iOS13+的SceneDelegate情况
如果你的项目是iOS13及以上,使用SceneDelegate管理窗口,那么应该把弹窗逻辑放到scene(_:willConnectTo:options:)方法里,并且确保scene的window已经设置并激活:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) window.rootViewController = YourRootViewController() self.window = window window.makeKeyAndVisible() // 弹窗 let alert = UIAlertController(title: "This is title", message: "This is message.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Ok", style: .default)) window.rootViewController?.present(alert, animated: true) }
总结一下,最可靠的方式是把弹窗逻辑放到rootViewController的viewDidAppear里,或者确保window完全激活后再执行present操作。
内容的提问来源于stack exchange,提问作者sulabh qg

