Search code examples
swiftuinavigationcontrollerroot

Not able to Navigate to root viewcontroller(Home) in swift


I have added NavigationControllerScene to ViewController and made it initial viewcontroller in storyboard

code: if I use below code tap on testBtn not able to go to ViewController1. why? i want show ViewController with sidemenu and bottom tabbar thats why i have written code like this. added added same code in Appdelegate and Scenedelagate as well

Why not able to Navigate from ViewController to ViewController1?

this is my info.plist

enter image description here

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    setUpNavigationBar(with: "Viewcontroller", isBackNeed: false)
}


@IBAction func testBtn(_ sender: UIButton){
    // added same code in Appdelegate and scenedelagete as well
    let home = Helper.getVcObject(vcName: .ViewController1, StoryBoardName: .Main) as! ViewController1
    let menu = Helper.getVcObject(vcName: .MenuViewController, StoryBoardName: .Main) as! MenuViewController
    let nav = MainNavigationController(rootViewController: home)
    let sideMenu = SideMenuController(contentViewController: nav, menuViewController: menu)
    Helper.replaceRootView(for: sideMenu)
    
}
}

and some of my custom methods:

let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
.first?.windows
.filter({$0.isKeyWindow}).first

enum StoryBoardNameCase: String {
case Main = "Main"

}

enum VCNameCase: String {
case MenuViewController = "MenuViewController"
case ViewController1 = "ViewController1"
}

class Helper {

static func getVcObject(vcName:VCNameCase, StoryBoardName:StoryBoardNameCase) -> UIViewController{
    
    let storyBoard: UIStoryboard = UIStoryboard(name: StoryBoardName.rawValue, bundle: nil)
    let vc = storyBoard.instantiateViewController(withIdentifier: vcName.rawValue)
    
    return vc
}

static func replaceRootView(for rootViewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
    if animated {
        UIView.transition(with: keyWindow ?? UIWindow(), duration: 0.5, options: .transitionCrossDissolve, animations: {
            let oldState: Bool = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            keyWindow?.rootViewController = rootViewController
            keyWindow?.makeKeyAndVisible()
            UIView.setAnimationsEnabled(oldState)
        }, completion: { (finished: Bool) -> () in
            completion?()
        })
    } else {
        keyWindow?.rootViewController = rootViewController
        keyWindow?.makeKeyAndVisible()
    }
}

EDIT: AppDelegate: code added same code in SceneDelegate as well

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    
    let home = Helper.getVcObject(vcName: .ViewController1, StoryBoardName: .Main) as! ViewController1
    let menu = Helper.getVcObject(vcName: .MenuViewController, StoryBoardName: .Main) as! MenuViewController
    let nav = MainNavigationController(rootViewController: home)
    let sideMenu = SideMenuController(contentViewController: nav, menuViewController: menu)
    Helper.replaceRootView(for: sideMenu)
    
    SideMenuController.preferences.basic.menuWidth = UIScreen.main.bounds.width * 0.8
    
    return true
}

SceneDelegate code:

 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).
        
    let home = Helper.getVcObject(vcName: .ViewController1, StoryBoardName: .Main) as! ViewController1
    let menu = Helper.getVcObject(vcName: .MenuViewController, StoryBoardName: .Main) as! MenuViewController
    let nav = MainNavigationController(rootViewController: home)
    let sideMenu = SideMenuController(contentViewController: nav, menuViewController: menu)
    Helper.replaceRootView(for: sideMenu)
    
    SideMenuController.preferences.basic.menuWidth = UIScreen.main.bounds.width * 0.8
    
}

Solution

  • if you are using in info plist from "Application Scene Manifest" you should define your root view controller in SceneDelegate:

    enter image description here

    So you define your LoginViewController As rootViewController on SceneDelegate

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        var window: UIWindow?
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            guard let scene = (scene as? UIWindowScene) else { return }
            self.window = UIWindow(windowScene: scene)
            window?.rootViewController = yourLoginVC
            window?.makeKeyAndVisible()
        }
    
        func initNewMainVC(rootVC: UIViewController) {
            window?.rootViewController = rootVC
            window?.makeKeyAndVisible()
        }
    }
    

    in your class:

    
    static func replaceRootView(for rootViewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
        let scene = UIApplication.shared.connectedScenes.first
            if let sceneDelegate: SceneDelegate = (scene?.delegate as? SceneDelegate) {
                sceneDelegate.initNewMainVC(rootVC: rootViewController)
            }
    }
    

    but if you use from AppDelegate means you don't use from SceneDelegate and don't have "Application Scene Manifest" in the info.plist:

    you should define all of that defined on SceneDelegate to Appdelegate and set it on appdelegate:

    class AppDelegate: UIResponder, UIApplicationDelegate {
        var window: UIWindow?
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            self.window = UIWindow(frame: UIScreen.main.bounds)
            self.window?.rootViewController = yourLoginVC
            self.window?.makeKeyAndVisible()
            return true
        }
    
    
        func initNewMainVC(rootVC: UIViewController) {
            window?.rootViewController = rootVC
            window?.makeKeyAndVisible()
        }
    }
    

    in your class:

    
    static func replaceRootView(for rootViewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) {
            if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
                appDelegate.initNewMainVC(rootVC: rootViewController)
            }
    }
    

    you should use One of them SceneDelegate or AppDelegate