iOS 13: Swift - 'Set application root view controller programmatically' does not work

旧城冷巷雨未停 提交于 2020-01-10 18:44:06

问题


I have following code in my AppDelegate.swift to setup root view controller for an iOS application. But it does not work. It follows Target structure (defined under General tab) and ignores this code.

(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
    }
}

Unable to understand where is the issue.

I tried following references also but no luck:

  • Swift ios set a new root view controller
  • Root View Controller Swift
  • iOS: Root Controller Navigation

回答1:


I tried following two options and both of these working for me. With iOS-13 (Xcode 11) a new file SceneDelegate.swift with the concept of UIWindowScene is enabled by default.

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()
    }
}

Alternate:

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()

    }
}

I don't know, why and how it works but it resolved my problem.

Reference docs that helped me:

  • init(windowScene:)
  • UIWindow
  • rootViewController



回答2:


I tried the following approach and it's working for me in iOS 13 and also tested on iOS 12.4.2 from Xcode 11.

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()
     }



回答3:


First Case

If major of your project is build in storyboard and before Xcode 11 is used for development and you are not using SwiftUI you want to use your old classes associated with AppDelegate.

  • Then try to remove "Application Scene Manifest" in info.pllist.
  • Remove ScenceDelegate from project completely.

    Then you will able to use your old code.

Second Case

If you want to use both Appdelegte and ScenceDelegate then below is working code.

App Delegate Code:

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 Code:

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

}




回答4:


var window: UIWindow?

has been moved from AppdDelegate.swift to SceneDelegate.swift.

So I used rootViewController in the Scene Delegate class and it works -

   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)
              }
    }



回答5:


Here's what work for both iOS 13.x and iOS 12.x and below

For iOS 13,In the Scene Delegate

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()
        }

In a utility class, I wrote below function to get the window object and assign it to the appdelegate.window. According to my needs, I needed to set root view controller at multiple places in different scenarios for which I needed the window object.

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()
    }
}

This worked well for me. Hopefully, it works for others too.




回答6:


I did two things. Firstly I set a Notification into SceneDelegate, then when I needed to change the RootViewController I did a Notification Post and it worked. But it's an anwful solution.

After that

My boss recommended me to change the Controllers of the NavigationController, something like this:

func logged() {
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "MainTabViewController", bundle: nil)
    let mainVC = mainStoryboard.instantiateInitialViewController()

    self.navigationController?.setViewControllers([mainVC!], animated: false)
}

I know that perhaps it's not the best solution, but I see it cleaner.
I'm working now on iOS 13 and I didn't want to use deprecated things.



来源:https://stackoverflow.com/questions/58084127/ios-13-swift-set-application-root-view-controller-programmatically-does-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!