Leaking Views When Changing Rootviewcontroller Inside Transitionwithview

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



Leave a reply



Submit