web-dev-qa-db-fra.com

iOS 13: Swift - «Définir le contrôleur de vue racine de l'application par programme» ne fonctionne pas

J'ai le code suivant dans mon AppDelegate.Swift pour configurer le contrôleur de vue racine pour une application iOS. Mais ça ne marche pas. Il suit la structure cible (définie sous l'onglet Général) et ignore ce 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
    }
}

Impossible de comprendre où est le problème.

J'ai aussi essayé de suivre les références mais pas de chance:

28
Krunal

J'ai essayé de suivre deux options et les deux fonctionnaient pour moi. Avec iOS-13 (Xcode 11), un nouveau fichier SceneDelegate.Swift avec le concept UIWindowScene est activé par défaut.

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

Alterner:

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

    }
}

Je ne sais pas, pourquoi et comment cela fonctionne mais cela a résolu mon problème.

Documents de référence qui m'ont aidé:

28
Krunal

J'ai essayé l'approche suivante et cela fonctionne pour moi dans iOS 13 et j'ai également testé sur iOS 12.4.2 de 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()
     }
17
Vikram Chaudhary

Pour choisir une approche précédente de celle prise en charge par SwiftUI, à partir d'un projet créé dans Xcode 11, vous pouvez suivre ces étapes.

Steps for get old aproach

11
Carlos García

Premier cas

Si la majeure partie de votre projet est intégrée dans le storyboard et avant que Xcode 11 ne soit utilisé pour le développement et que vous n'utilisez pas SwiftUI, vous souhaitez utiliser vos anciennes classes associées à AppDelegate.

  • Essayez ensuite de supprimer "Application Scene Manifest" dans info.pllist.
  • Supprimez complètement ScenceDelegate du projet.

    Ensuite, vous pourrez utiliser votre ancien code.

Deuxième cas

Si vous souhaitez utiliser à la fois Appdelegte et ScenceDelegate, le code fonctionne ci-dessous.

Code de délégué d'application:

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
}

Code 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
}
7
Gurpreet Singh

Voici ce qui fonctionne pour iOS 13.x et iOS 12.x et inférieur

Pour iOS 13, Délégué In the Scene

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

Dans une classe utilitaire, j'ai écrit ci-dessous la fonction pour obtenir l'objet window et l'assigner à appdelegate.window. Selon mes besoins, je devais définir le contrôleur de vue racine à plusieurs endroits dans différents scénarios pour lesquels j'avais besoin de l'objet fenêtre.

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

Cela a bien fonctionné pour moi. Espérons que cela fonctionne aussi pour les autres.

3
Hemant Bavle
var window: UIWindow?

a été déplacé d'AppdDelegate.Swift vers SceneDelegate.Swift.

J'ai donc utilisé rootViewController dans la classe Scene Delegate et cela fonctionne -

   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)
              }
    }
3
Aditya Ahuja

J'ai fait deux choses. Tout d'abord, j'ai défini un Notification en SceneDelegate, puis quand j'ai eu besoin de changer le RootViewController j'ai fait un Notification Post et cela a fonctionné. Mais c'est une solution angoissante.

Après ça

Mon patron m'a recommandé de changer le Controllers du NavigationController, quelque chose comme ceci:

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

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

Je sais que ce n'est peut-être pas la meilleure solution, mais je la vois plus propre.
Je travaille maintenant sur iOS 13 et je ne voulais pas utiliser deprecated choses.

0
unferna