Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Switch root view controller
func switchRootViewController(rootViewController: UIViewController, animated: Bool, completion: (() -> Void)?) {
if animated {
UIView.transitionWithView(window, duration: 0.5, options: .TransitionCrossDissolve, animations: {
let oldState: Bool = UIView.areAnimationsEnabled()
UIView.setAnimationsEnabled(false)
self.window!.rootViewController = rootViewController
UIView.setAnimationsEnabled(oldState)
}, completion: { (finished: Bool) -> () in
if completion {
completion!()
}
})
} else {
window!.rootViewController = rootViewController
}
}
@braamin
Copy link

braamin commented Jun 8, 2016

Really useful, thanks.

Loading

@ucotta
Copy link

ucotta commented Jun 5, 2017

Nice method!, thanks

Loading

@mitchins
Copy link

mitchins commented Jul 18, 2017

Works a treat

Loading

@abdelmajidrajad
Copy link

abdelmajidrajad commented Oct 26, 2017

Very useful method, thank you

Loading

@omaralbeik
Copy link

omaralbeik commented Dec 17, 2017

Thanks, very useful :)

Here is a Swift 4 version

extension UIWindow {
	
	func switchRootViewController(_ viewController: UIViewController,  animated: Bool = true, duration: TimeInterval = 0.5, options: UIViewAnimationOptions = .transitionFlipFromRight, completion: (() -> Void)? = nil) {
		guard animated else {
			rootViewController = viewController
			return
		}
		
		UIView.transition(with: self, duration: duration, options: options, animations: {
			let oldState = UIView.areAnimationsEnabled
			UIView.setAnimationsEnabled(false)
			self.rootViewController = viewController
			UIView.setAnimationsEnabled(oldState)
		}) { _ in
			completion?()
		}
	}
}

Loading

@anirudhamahale
Copy link

anirudhamahale commented Jan 8, 2018

One more variation, use this as global function.

func switchRootViewController(rootViewController: UIViewController, animated: Bool, completion: (() -> Void)?) {
    guard let window = UIApplication.shared.keyWindow else { return }
    if animated {
        UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
            let oldState: Bool = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            window.rootViewController = rootViewController
            UIView.setAnimationsEnabled(oldState)
        }, completion: { (finished: Bool) -> () in
            if (completion != nil) {
                completion!()
            }
        })
    } else {
        window.rootViewController = rootViewController
    }
}

Loading

@munepom
Copy link

munepom commented Mar 7, 2018

Nice work! Thanks!!! 🍎

Loading

@bfwg
Copy link

bfwg commented Jun 8, 2018

Make completion parameter optional.

    func switchRootViewController(rootViewController: UIViewController, animated: Bool, completion: (() -> Swift.Void)? = nil) {
      ...
    )

Loading

@piv199
Copy link

piv199 commented Oct 18, 2019

Warning: 'keyWindow' was deprecated in iOS 13.0: Should not be used for applications that support multiple scenes as it returns a key window across all connected scenes

Loading

@kpavankotesh
Copy link

kpavankotesh commented Dec 3, 2019

What is the replacement for this? Any solution?

Loading

@bcmbf14
Copy link

bcmbf14 commented Feb 26, 2020

try this!

    let scene = UIApplication.shared.connectedScenes.first
    guard let windowScene = (scene as? UIWindowScene) else { return }
    let window = UIWindow(windowScene: windowScene)

Loading

@Bartozo
Copy link

Bartozo commented Mar 24, 2020

My solution is very simple. Inside your SceneDelegate.swift file add your function to update root view controller.
For example this is my function to change root widow to tab bar controller.

// SceneDelegate.swift

    func moveToTabBarController() {
        window?.rootViewController = createTabBar()
        window?.makeKeyAndVisible()
    }

And inside your view controller you can get access to scene delegate and call your change controller function.

   let sceneDelegate = self.view.window?.windowScene?.delegate as! SceneDelegate
   sceneDelegate.moveToTabBarController()

Loading

@AakifNadeem
Copy link

AakifNadeem commented Jul 31, 2020

If you have sceneDelegate in your project use this:

let sb = UIStoryboard(name: "Main", bundle: nil)
let VC = sb.instantiateViewController(withIdentifier: "LoginViewController") as! LoginViewController
let navRootView = UINavigationController(rootViewController: VC)
self.present(navRootView, animated: true, completion: nil)

Loading

@ahmedsafadii
Copy link

ahmedsafadii commented Sep 19, 2020

Swift 5.1

extension UIWindow {
    
    func switchRootViewController(_ viewController: UIViewController,
                                  animated: Bool = true,
                                  duration: TimeInterval = 0.5,
                                  options: AnimationOptions = .transitionFlipFromRight,
                                  completion: (() -> Void)? = nil) {
        guard animated else {
            rootViewController = viewController
            return
        }
        
        UIView.transition(with: self, duration: duration, options: options, animations: {
            let oldState = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            self.rootViewController = viewController
            UIView.setAnimationsEnabled(oldState)
        }, completion: { _ in
            completion?()
        })
    }
    
}

Loading

@jesus003
Copy link

jesus003 commented Sep 28, 2020

Swift 5.1

extension UIWindow {
    
    func switchRootViewController(_ viewController: UIViewController,
                                  animated: Bool = true,
                                  duration: TimeInterval = 0.5,
                                  options: AnimationOptions = .transitionFlipFromRight,
                                  completion: (() -> Void)? = nil) {
        guard animated else {
            rootViewController = viewController
            return
        }
        
        UIView.transition(with: self, duration: duration, options: options, animations: {
            let oldState = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            self.rootViewController = viewController
            UIView.setAnimationsEnabled(oldState)
        }, completion: { _ in
            completion?()
        })
    }
    
}

how I can implement this ?

Loading

@lexuanquynh
Copy link

lexuanquynh commented Mar 16, 2021

@jesus003
let loginVC = QGLoginViewController.loadFromNib()
guard let window = self.view.window else {
return
}
window.switchRootViewController(to: loginVC)

Loading

@joseph-elmallah
Copy link

joseph-elmallah commented Oct 14, 2021

Swift 5.5 with the introduction of async/await APIs

extension UIWindow {

    @MainActor
    func setRootViewController(_ newRootViewController: UIViewController, animated: Bool = true) async {
        guard animated else {
            rootViewController = newRootViewController
            return
        }

        await withCheckedContinuation({ (continuation: CheckedContinuation<Void, Never>) in
            UIView.transition(with: self, duration: 0.3, options: .transitionCrossDissolve) {
                let oldState: Bool = UIView.areAnimationsEnabled
                UIView.setAnimationsEnabled(false)
                self.rootViewController = newRootViewController
                UIView.setAnimationsEnabled(oldState)
            } completion: { _ in
                continuation.resume()
            }
        })
    }

}

Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment