Skip to content

Instantly share code, notes, and snippets.

@nvkiet
Last active June 21, 2023 12:35
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save nvkiet/6368d1d45c4ea3e6d9cb to your computer and use it in GitHub Desktop.
Save nvkiet/6368d1d45c4ea3e6d9cb to your computer and use it in GitHub Desktop.
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.

@ucotta
Copy link

ucotta commented Jun 5, 2017

Nice method!, thanks

@mitchins
Copy link

Works a treat

@abdelmajidrajad
Copy link

Very useful method, thank you

@omaralbeik
Copy link

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

@anirudhamahale
Copy link

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

@munepom
Copy link

munepom commented Mar 7, 2018

Nice work! Thanks!!! 🍎

@bfwg
Copy link

bfwg commented Jun 8, 2018

Make completion parameter optional.

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

@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

@kpavankotesh
Copy link

What is the replacement for this? Any solution?

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

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

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

@ahmedsafadii
Copy link

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

@jesus003
Copy link

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 ?

@lexuanquynh
Copy link

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

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

}

@zzbhagyalakshmi
Copy link

Crash Occurrence in this method, not regular. Any Idea to fix this sporadic crash..

NSInternalInconsistencyException - Attempting to transfer an animation to an animation state that is not a direct child of the animation's animation state.

Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x92d1c __exceptionPreprocess
1 libobjc.A.dylib 0x14ee4 objc_exception_throw
2 Foundation 0x124c80 _userInfoForFileAndLine
3 UIKitCore 0x5f0658 -[UIViewAnimationState _transferAnimationToTrackingAnimator:]
4 UIKitCore 0x71ee0 __85+[UIViewPropertyAnimator _animationBlockForTrackingAnimation:animator:trackingSetup:]_block_invoke
5 UIKitCore 0x4a3d14 -[UIViewPropertyAnimator _runAnimations]
6 UIKitCore 0x34fba4 __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke_2
7 UIKitCore 0x206ccc __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke
8 UIKitCore 0x70754c __49-[UIViewPropertyAnimator startAnimationAsPaused:]_block_invoke.1011
9 UIKitCore 0x510eac -[UIViewPropertyAnimator _setupAnimationTracking:]
10 UIKitCore 0x9c494 -[UIViewPropertyAnimator startAnimationAsPaused:]
11 UIKitCore 0x3396e4 +[UIViewPropertyAnimator _trackAnimationWithAnimator:forLayer:forAnimationKey:trackingSetup:]
12 UIKitCore 0x9c07c +[UIViewPropertyAnimator _trackNonAdditiveAnimationWithAnimator:forLayer:forKey:]
13 UIKitCore 0x58d55c -[UIViewAnimationState pop]
14 UIKitCore 0x25bdb8 +[UIViewAnimationState popAnimationState]
15 UIKitCore 0x1751a8 +[UIView _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:]

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