Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController lifecycle broken iOS13, makeKeyAndVisible() seems not to operate?

I have a custom UIStoryboardSegue that works as desired in iOS12.*.

One of the destination view controller is a UITabbarController: for each tab, I have a controller embedded in a navigation controller.

Unfortunately, for iOS13.*, this does not work well: the view controller lifecycle is broken, and no call the viewXXXAppear() nor the willTransition() methods are no longer issued.

It looks like makeKeyAndVisible() has no effect?!

See at the bottom how the screen UI is puzzled below without viewWillAppear() being called.

An horrible temporary workaround

I had to pull my hairs but, I have found a fix which I make public (I had to add a navigation controller on the fly).

This messes the vc hierarchy: do you have a better solution?

public class AladdinReplaceRootViewControllerSegue: UIStoryboardSegue {
    override public func perform() {

        guard let window = UIApplication.shared.delegate?.window as? UIWindow,
            let sourceView = source.view,
            let destinationView = destination.view else {
            super.perform()
            return
        }

        let screenWidth  = UIScreen.main.bounds.size.width
        let screenHeight = UIScreen.main.bounds.size.height

        destinationView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)

        window.insertSubview(destinationView, aboveSubview: sourceView)


        // **My fix**
        if #available(iOS 13,*) {
            // I introduced an invisible navigation controller starting in iOS13 otherwise, my controller attached to the tabbar thru a navigation, dont work correctly, no viewXAppearis called.
            let navigationController = UINavigationController.init(rootViewController: self.destination)
            navigationController.isNavigationBarHidden = true
            window.rootViewController = navigationController
        }
        else {
            window.rootViewController = self.destination
        }

        window.makeKeyAndVisible()
    }
}

UI destroyed

like image 355
Stéphane de Luca Avatar asked Oct 13 '19 02:10

Stéphane de Luca


1 Answers

I found a solution thanks to Unbalanced calls to begin/end appearance transitions with custom segue What happens here is that the creation and attaching of the destination view controller happens twice, and the first one happens too soon. So what you need to do is:

public class AladdinReplaceRootViewControllerSegue: UIStoryboardSegue {
    override public func perform() {

        guard let window = UIApplication.shared.delegate?.window as? UIWindow,
            let sourceView = source.view,
            let destinationView = destination.view else {
            super.perform()
            return
        }

        let screenWidth  = UIScreen.main.bounds.size.width
        let screenHeight = UIScreen.main.bounds.size.height

        let mock = createMockView(view: desination.view)
        window.insertSubview(mock, aboveSubview: sourceView)

        //DO SOME ANIMATION HERE< MIGHT NEED TO DO mock.alpha = 0

        //after the animation is done:
        window.rootViewController = self.destination
        mock.removeFromSuperview()
    }

    func createMockView(view: UIView) -> UIImageView {
        UIGraphicsBeginImageContextWithOptions(view.frame.size, true, UIScreen.main.scale)

        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()
        return UIImageView(image: image)    
    }   
}
like image 137
mesh Avatar answered Jan 04 '23 14:01

mesh