2

I'm running into an issue where after changing the rootViewController on my UINavigationController and changing it back to my original UINavigationController, a UISplitViewController begins to show both it's master and detail view in a phone device on compact/portrait orientation (so not only on plus size phones, but also others).

Basic overview of architecture: A TabBarController houses several tabs. One of these tabs is a UISplitViewController. I currently override the following to ensure that the MasterViewController is shown on compact orientations:

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {

    // this prevents phone from going straight to detail on showing the split view controller
    return true
}

This works fine and displays the master on portrait as expected. At any point pressing a button on another tab can create a new UINavigationController instance and display it, in which I'm doing the below to change the rootViewController to the newly created UINavigationController to display:

let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = newNavVC

On dismiss, I'm just swapping the UINavigationController back to the original one through the same code above. However, once I do this one time (create nav/display/dismiss), and I switch my tab back to the one with the UISplitViewController, it changes itself to show a side-by-side master detail view. I didn't know this was possible in portrait mode for compact sizing. I tried changing to any of the 4 preferred display modes in the UISplitViewController, but that didn't fix it.

Below is what it looks like (iPhone 6 simulator), am I missing delegates or misunderstanding collapsing?

Before:

enter image description here

After:

enter image description here

shim
  • 9,289
  • 12
  • 69
  • 108
Hellojeffy
  • 1,851
  • 2
  • 19
  • 23

1 Answers1

2

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()
        }
    }
}
Community
  • 1
  • 1