View controller retaining another when changing rootViewController
You are messing up the view hierarchy here. You should dismiss all the presented view controllers of the current rootViewController
before switching to the new one. This is the only solution I've found to work!
Your switchRootViewController
method should be like below,
func switchRootViewController() {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.dismiss(animated: true, completion: {
let vc = getTheOtherViewController()
self.window?.rootViewController = vc
})
}
}
If you there are multiple view controllers presented,
func switchRootViewController() {
self.view.window!.rootViewController?.dismiss(animated: true, completion: {
let vc = getTheOtherViewController()
self.window?.rootViewController = vc
})
}
Replacing the UIWindow's rootViewController while using a transition, appears to be leaking
I logged that bug report; I have had no response from Apple engineering on it.
The sample code I submitted with the bug report demonstrating the issue is at https://github.com/adurdin/radr21404408
As far as I am aware, the issue is still present in current versions of iOS, but I have not tested exhaustively. Who knows, perhaps 9.3 beta fixes it? :)
In the application where I encountered this bug, we had been using custom transitions and rootViewController replacement for the majority of screen transitions. I have not found a solution to this leak, and because of reasons could not easily remove all the rootViewController manipulation, so instead worked around the issue by minimising where we used presentViewController and friends, and carefully managing the places where we required it.
One approach that I think has potential to avoid the bug while still retaining similar capabilities to rootViewController swapping--but have not yet implemented--is to have the rootViewController be a custom container view controller that occupies the full screen, and defines a presentation context. Instead of swapping the window's rootViewController, I would swap the single child view controller in this container. And because the container defines the presentation context, the presentations will occur from the container instead of the child being swapped. This should then avoid the leaks.
Changing rootViewController for Nav causes UISplitViewController to show detail on Compact portrait orientation
You can replace the the logic that assigned the rootViewController with the code snippet found at this link:
Leaking views when changing rootViewController inside transitionWithView
Basically you just create an extension for the UIWindow class that will set the root view controller correctly.
extension UIWindow {
/// Fix for https://stackoverflow.com/a/27153956/849645
func set(rootViewController newRootViewController: UIViewController, withTransition transition: CATransition? = nil) {
let previousViewController = rootViewController
if let transition = transition {
// Add the transition
layer.add(transition, forKey: kCATransition)
}
rootViewController = newRootViewController
// Update status bar appearance using the new view controllers appearance - animate if needed
if UIView.areAnimationsEnabled {
UIView.animate(withDuration: CATransaction.animationDuration()) {
newRootViewController.setNeedsStatusBarAppearanceUpdate()
}
} else {
newRootViewController.setNeedsStatusBarAppearanceUpdate()
}
/// The presenting view controllers view doesn't get removed from the window as its currently transistioning and presenting a view controller
if let transitionViewClass = NSClassFromString("UITransitionView") {
for subview in subviews where subview.isKind(of: transitionViewClass) {
subview.removeFromSuperview()
}
}
if let previousViewController = previousViewController {
// Allow the view controller to be deallocated
previousViewController.dismiss(animated: false) {
// Remove the root view in case its still showing
previousViewController.view.removeFromSuperview()
}
}
}
RootViewController Switch Transition Animation
You can wrap the switching of the rootViewController
in a transition animation block:
[UIView transitionWithView:self.window
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{ self.window.rootViewController = newViewController; }
completion:nil];
Related Topics
iOS Swift: How to Change the Font Style of a Certain Word in a String
After Updating to Xcode 10.2: Invalid Toolchain Error When Trying to Submit App to App Store
Location Access - App Is Not Asking for User Permission to Access Location - iOS 11
Tracking Mkmapview Centercoordinate While Panning
Error: "Array Index Out of Range" in Multidimensional Array
How to Enable "Tap and Slide" in a Uislider
Swift - Checking Unmanaged Address Book Single Value Property for Nil
Exit Application When Click Button - iOS
Swift Optional Chaining Doesn't Work in Closure
Error in Xcode 6 - View Controller Does Not Have an Outlet Named (Subview)
How to Change the Color of Uipickerview Selector
Fatal Error: Nsarray Element Failed to Match the Swift Array Element Type
Disabling Firebase Automatic Screen Reporting
Can't Submit Apps to Appstore: Error Itms-90534: "Invalid Toolchain