【问题标题】:iOS 13: Swift - 'Set application root view controller programmatically' does not workiOS 13:Swift - “以编程方式设置应用程序根视图控制器”不起作用
【发布时间】:2020-01-24 19:17:36
【问题描述】:

我的 AppDelegate.swift 中有以下代码来设置 iOS 应用程序的根视图控制器。但它不起作用。它遵循 Target 结构(在 General 选项卡下定义)并忽略此代码。

(Xcode 11、Swift 5.1、iOS 13)

class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        window = UIWindow(frame: UIScreen.main.bounds)
        guard let rootVC = UIViewController() else {
            print("Root VC not found")
            return true
        }
        let rootNC = UINavigationController(rootViewController: rootVC)
        window?.rootViewController = rootNC
        window?.makeKeyAndVisible()

        return true
    }
}

无法理解问题出在哪里。

我也尝试了以下参考,但没有运气:

【问题讨论】:

  • “不工作”不足以描述问题。
  • 你用什么版本的 iOS 测试这个?
  • 在 iOS 12 中像这样设置根视图控制器已经结束。您需要使用 SceneDelegate。
  • 阅读 Scenes 文档并观看 WWDC 2019 的相关视频。
  • 对于寻找支持 iOS 12 和 13 以及支持情节提要或全代码用户界面的更新“单一视图应用程序”Xcode 模板的任何人,请参阅github.com/rmaddy/XcodeTemplates

标签: ios swift appdelegate ios13 rootviewcontroller


【解决方案1】:

要从在 Xcode 11 中创建的项目中选择 SwiftUI 支持的先前方法,您可以按照以下步骤操作。

【讨论】:

  • 如果您不打算使用 SwiftUI 或 SceneDelegate.swift 文件只会占用不必要的空间,这是一个很好的解决方案。
  • 这确实是最好的解决方案!
  • 这使我的应用程序崩溃,说:“应用程序窗口在应用程序启动结束时应该有一个根视图控制器”
  • 哇,我花了 2 天时间才找到这个解决方案。谢谢!
【解决方案2】:

我尝试了以下两个选项,这两个选项都对我有用。在 iOS-13 (Xcode 11) 中,默认启用带有 UIWindowScene 概念的新文件 SceneDelegate.swift。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        guard let windowScene = (scene as? UIWindowScene) else { return }


        self.window = UIWindow(windowScene: windowScene)
        //self.window =  UIWindow(frame: UIScreen.main.bounds)

        let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
        guard let rootVC = storyboard?.instantiateViewController(identifier: "ViewControllerIdentifierName") as? ViewController else {
            print("ViewController not found")
            return
        }
        let rootNC = UINavigationController(rootViewController: rootVC)
        self.window?.rootViewController = rootNC
        self.window?.makeKeyAndVisible()
    }
}

替代:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let windowScene = UIWindowScene(session: session, connectionOptions: connectionOptions)
        self.window = UIWindow(windowScene: windowScene)
        //self.window =  UIWindow(frame: UIScreen.main.bounds)
        let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
        guard let rootVC = storyboard?.instantiateViewController(identifier: "ViewControllerIdentifierName") as? ViewController else {
            print("ViewController not found")
            return
        }
        let rootNC = UINavigationController(rootViewController: rootVC)
        self.window?.rootViewController = rootNC
        self.window?.makeKeyAndVisible()

    }
}

我不知道它为什么以及如何工作,但它解决了我的问题。

对我有帮助的参考文档:

【讨论】:

  • 如何从任何 UIViewController(例如 HomeVC/LoginVC)更改 RootViewController?
  • @JD。如果您正在尝试使用 iOS 13,请先在 SceneDelegate 的帮助下获取 Window 场景对象,然后是 window 对象(使用类似于上述答案的源代码),然后是用于设置导航控制器的常规代码。
  • 如何获取windowScene实例?我正在尝试访问 SceneDelegate.swift 上的“窗口”。但没有得到。
  • 使用未解析的标识符“AppConstants”。 AppConstant 在哪里?
  • @mumu - 感谢您强调这个问题。我现在已经更正了。
【解决方案3】:

我尝试了以下方法,它在 iOS 13 中对我有用,并且还在 Xcode 11 的 iOS 12.4.2 上进行了测试。

func resetRoot() {
            guard let rootVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as? ViewController else {
                return
            }
            let navigationController = UINavigationController(rootViewController: rootVC)

            UIApplication.shared.windows.first?.rootViewController = navigationController
            UIApplication.shared.windows.first?.makeKeyAndVisible()
     }

【讨论】:

  • 这显示了弃用 keyWindow 的警告。另一个选项是“UIApplication.shared.windows.first?.rootViewController”
  • 不。窗口计数为零。为了让它工作,我必须创建一个实例 UIWindow() 并将它分配给委托的 window prop,然后它开始在 12.x 版本中工作。
  • @HemantBavle 您能否详细说明您是如何完成这项工作的,它是否适用于 ios 12 和 13?
  • @Stotch 我刚刚在 AppDelegate 中添加了上述函数,并通过 AppDelegate 的实例使用该函数。它在 iOS 12 和 13 中都运行良好
  • 优秀的回答它的工作 100% 感谢您节省了我的半天时间
【解决方案4】:

以下内容适用于 iOS 13.x 和 iOS 12.x 及更低版本

对于 iOS 13,在场景委托中

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            guard let windowScene = (scene as? UIWindowScene) else { return }
            self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
           //Make sure to do this else you won't get 
           //the windowScene object using UIApplication.shared.connectedScenes
            self.window?.windowScene = windowScene 
            let storyBoard: UIStoryboard = UIStoryboard(name: storyBoardName, bundle: nil)
            window?.rootViewController = storyBoard.instantiateInitialViewController()
            window?.makeKeyAndVisible()
        }

在一个实用程序类中,我编写了下面的函数来获取window 对象并将其分配给appdelegate.window。根据我的需要,我需要在需要window对象的不同场景的多个地方设置根视图控制器。

static func redirectToMainNavRVC(currentVC: UIViewController){
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        let vc = UIStoryboard(name: appDelegate.storyBoardName, bundle: nil).instantiateViewController(withIdentifier: "MainNavigationViewController") as! MainNavigationViewController
    if #available(iOS 13.0, *){
        if let scene = UIApplication.shared.connectedScenes.first{
            guard let windowScene = (scene as? UIWindowScene) else { return }
            print(">>> windowScene: \(windowScene)")
            let window: UIWindow = UIWindow(frame: windowScene.coordinateSpace.bounds)
            window.windowScene = windowScene //Make sure to do this
            window.rootViewController = vc
            window.makeKeyAndVisible()
            appDelegate.window = window
        }
    } else {
        appDelegate.window?.rootViewController = vc
        appDelegate.window?.makeKeyAndVisible()
    }
}

这对我来说效果很好。希望它也适用于其他人。

【讨论】:

    【解决方案5】:

    第一个案例

    如果您的项目主要是在 Storyboard 中构建并且在使用 Xcode 11 进行开发之前并且您没有使用 SwiftUI,那么您希望使用与 AppDelegate 关联的旧类。

    • 然后尝试删除 info.pllist 中的“Application Scene Manifest”。
    • 从项目中完全移除 ScenceDelegate。

      然后你就可以使用你的旧代码了。

    第二种情况

    如果你想同时使用 Appdelegte 和 ScenceDelegate 那么下面是工作代码。

    应用代理代码:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {   
     if #available(iOS 13.0, *){
        //do nothing we will have a code in SceneceDelegate for this 
    } else {
        let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let VC = mainStoryboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
        navigationController?.isNavigationBarHidden = true
        navigationController = UINavigationController(rootViewController: VC)
        navigationController?.isNavigationBarHidden = true // or not, your choice.
        self.window = UIWindow(frame: UIScreen.main.bounds)
        self.window!.rootViewController = navigationController
    }
    return true
    }
    

    ScenceDelegate 代码:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    
        let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
        let VC = mainStoryboard.instantiateViewController(withIdentifier: "LoginVC") as! LoginVC
        navigationController?.isNavigationBarHidden = true
        guard let windowScene = (scene as? UIWindowScene) else { return }
        self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
        window.windowScene = windowScene
        window.rootViewController = VC
        window.makeKeyAndVisible()
        let appDelegate = UIapplication.shared.delegate as! AppDelegate
        appDelegate.window = window
    }
    

    【讨论】:

      【解决方案6】:

      如果你关注这个答案https://stackoverflow.com/a/58084612/6667477

      如果您以编程方式初始化 rootController,您还必须从以下位置删除对主情节提要的引用:

      1. Info.plist.

      1. 部署信息

      【讨论】:

        【解决方案7】:
        var window: UIWindow?
        

        已从 AppdDelegate.swift 移至 SceneDelegate.swift。

        所以我在 Scene Delegate 类中使用了 rootViewController 并且它可以工作 -

           func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
                // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
                // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
                // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
                guard let _ = (scene as? UIWindowScene) else { return }
        
                if let tabBarController = window?.rootViewController as? UITabBarController {
                          let storyboard = UIStoryboard(name: "Main", bundle: nil)
                          let vc = storyboard.instantiateViewController(identifier: "NavController")
        
                          vc.tabBarItem = UITabBarItem(tabBarSystemItem: .topRated, tag: 1)
                          tabBarController.viewControllers?.append(vc)
                      }
            }
        

        【讨论】:

          【解决方案8】:

          适用于 SceneDelegate.swift

          中的 Xcode 11+ 和 Swift 5+
          
          var window: UIWindow?
          
              func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
                  guard let windowScene = scene as? UIWindowScene else { return }
                  let window = UIWindow(windowScene: windowScene)
                  let submodules = (
                      home: HomeRouter.createModule(),
                      search: SearchRouter.createModule(),
                      exoplanets: ExoplanetsRouter.createModule()
                  )
                      
                  let tabBarController = TabBarModuleBuilder.build(usingSubmodules: submodules)
                      
                  window.rootViewController = tabBarController
                  self.window = window
                  window.makeKeyAndVisible()
              }
          

          【讨论】:

            【解决方案9】:

            如果您不想使用主情节提要,如果您想以编程方式创建自己的视图。

            对于 xcode 11。

            func scene(_scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

                guard let windowScene = (scene as? UIWindowScene) else { return }
                window = UIWindow(windowScene: windowScene)
                window?.makeKeyAndVisible()
                window?.rootViewController = UINavigationController(rootViewController:      ViewController())
            }
            

            这绝对有效。谢谢

            【讨论】:

            • 与现有答案stackoverflow.com/a/58084612/341994完全一样
            • @matt。同意!但行数减少。感谢您的建议。
            • 如果你不使用情节提要,这会更好更干净。
            【解决方案10】:
            func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            
                guard let windowScene = (scene as? UIWindowScene) else { return }
                self.window = UIWindow(windowScene: windowScene)               
                  self.window?.makeKeyAndVisible()
            
                let layout = UICollectionViewFlowLayout()
                let swipingController = SwipingController(collectionViewLayout: layout)
                      self.window?.rootViewController = swipingController       
            
            }
            

            【讨论】:

            • 欢迎来到 Stack Overflow。请考虑为您的代码提供一些解释/评论,以便读者能够理解您所做的事情。
            【解决方案11】:

            我刚刚删除了 info.plist 中关于应用场景的一行。对我有用。

            【讨论】:

              【解决方案12】:
                  let MainViewController  = mainStoryboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
              
                                  let nvc:rootNavigation  = mainStoryboard.instantiateViewController(withIdentifier: "rootNavigation") as! rootNavigation
                                  nvc.viewControllers = [Mainmap]
                                 
                                  leftViewController.mainViewController = nvc
              
              UIApplication.shared.windows.first?.backgroundColor = UIColor(red: 236.0, green: 238.0, blue: 241.0, alpha: 1.0)
                                  UIApplication.shared.windows.first?.rootViewController = nvc
                                  UIApplication.shared.windows.first?.makeKeyAndVisible()
              

              【讨论】:

                【解决方案13】:
                let controller = ....
                let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
                keyWindow?.rootViewController = controller
                keyWindow?.makeKeyAndVisible()
                

                这些是设置根视图控制器所需的基本代码。

                【讨论】:

                  【解决方案14】:

                  重要注意事项: 如果您打算将 SwiftUI 添加到现有项目,则项目的部署目标必须设置为 iOS 13,然后项目必须具有 SceneDelegate。您不能仅使用 AppDelegate 启动应用程序。 (如果您不打算使用 SwiftUI,但仍需要最小部署目标为 iOS 13,请按照上面 Carlos García 的回答)

                  如果您尝试在 SwiftUI 中添加代码,请确保在已创建的项目中

                  • 最低部署目标设置为 iOS 13
                  • 您已正确设置 Appdelegate + SceneDelegate
                  • 主要是在 Info.plist 中有 Application Scene Manifest 条目,可能会创建一个虚拟项目并从那里复制条目

                  【讨论】:

                    【解决方案15】:

                    我做了两件事。首先,我将Notification 设置为SceneDelegate,然后当我需要更改RootViewController 时,我做了Notification Post,它起作用了。但这是一个糟糕的解决方案。

                    之后

                    我的老板建议我把NavigationControllerControllers改成这样:

                    func logged() {
                        let mainStoryboard: UIStoryboard = UIStoryboard(name: "MainTabViewController", bundle: nil)
                        let mainVC = mainStoryboard.instantiateInitialViewController()
                    
                        self.navigationController?.setViewControllers([mainVC!], animated: false)
                    }
                    

                    我知道这可能不是最好的解决方案,但我认为它更干净。
                    我现在在 iOS 13 上工作,我不想使用 deprecated 的东西。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-02-10
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多