presenting view controllers; errors involved

Hello,


In the simulator (iOS 12.2, multiple phone versions tried), I had some code which presented one view on top of another, using


present(childVC, animated: false, completion: nil)


where childVC is the view controller for the new view. Even though this view had had its windowframe set to the same frame as the window that contained the original view, the new view appeared smaller (in the upper left corner of the screen) with the old view taking up the rest of the space behind it. I couldn't figure out the cause of this, so I worked around it by setting the rootViewController of the window of the application delegate to the new view, which worked fine, except that when going back to the original view (dismissing childVC) and presenting another new view on top of it, I would get the message "Presenting view controllers on detached view controllers is discouraged".


Now working on iOS 13.1, this workaround does not work. It results in a blank screen, after the view pops up briefly; viewWillDisappear is getting called for some reason. If I take out the part setting rootViewController of the window to childVC, then it works fine.


I will note that these errors affect only the first view controller presented (after the original view controller that was originally the rootViewController of the window). If I move on to another view by pressing one of the buttons in my new view (in the case of the black screen, this can be done by setting a break point before the screen goes black and clicking on one of the buttons before continuing), then everything works as expected.


I am looking for some insight into these errors; any help appreciated.

Accepted Reply

class PresentedBaseClass : UIView{
    override init(frame: CGRect){
        super.init(frame: frame)
        setupSubviews()
    }

    func setupSubviews(){
        self.translatesAutoresizingMaskIntoConstraints = false // This is the problematic line

        // ... some code to create subviews
   
        setupConstraints()
    }

    func setupConstraints(){
        NSLayoutConstraint.activate([
            // Adding the two lines of code below will correct the problem, although
            // it is probably better to remove the problematic line
            //self.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width)
            //self.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height)

            // ... constraints on subviews
        ])
    }
}

class PresentedSubClass1 : PresentedBaseClass{
    // ...
}

class PresentedSubClass2 : PresentedBaseClass{
    // ...
}

class PresentingClass : UIView{
var windowFrame: CGRect
   
    init(windowframe: CGRect){
        windowFrame = windowframe
        super.init(nibName: nil, bundle: nil)
    }

    @objc func originalPresentation(_ sender: UIButton){
        childVC = PresentedSubClass1Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        // The two lines below are the original workaround
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }

    @objc func gotoSubClass1(){
        childVC?.dismiss(animated: false, completion: nil)
        childVC = PresentedSubClass1Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }
   
    @objc func gotoSubClass2(){
        childVC?.dismiss(animated: false, completion: nil)
        childVC = PresentedSubClass2Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }

}

class AppDelegate: UIResponder, UIApplicationDelegate{
    var window: UIWindow?
    var PresentingController: PresentingController!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
       
        PresentingController = PresentingController(windowframe: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
       
        window?.rootViewController = PresentingController
        window?.makeKeyAndVisible()
        return true
    }
}

Apparently, the problem was that the presented view was not properly constrained; its subviews were, but it wasn't. See code above.

Replies

Look in IB at how the Presentation is defined for the view, notably child view And the parent. If it is automatic, change to Full screen. You could then remove the workaround you had implemented and see happens.

I'm fairly sure that the presenting view controller has to be and remain the rootViewControlller during a presentation

> the presenting view controller has to be and remain the rootViewControlller during a presentation



I believe this is wrong. This code works for children:


    UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
    while( parentController.presentedViewController &&
          parentController != parentController.presentedViewController ){
        parentController = parentController.presentedViewController;
    }
    [parentController presentViewController:alert animated:YES completion:nil];

> Even though this view had had its windowframe set to the same frame as the window that contained the original view,


When you fiddle with the windowframe does the presented view controller change in some predictable or unpredictable way?

Show us some code - how are you presenting the child view controller? How do you set the frame?

The automatic modalPresentationStyle is an iOS 13 thing. I get a compilation error if I try to set the modalPresentationStyle to automatic on iOS 12, thus it can not be the source of the issue I am experiencing on iOS 12. Thanks for your reply though.

I actually get better behavior on iOS 12 if I change the rootViewController to the presented (vs presenting) view controller. What you say might be true on iOS 13, which could be why this workaround doesn't work there.

class PresentedBaseClass : UIView{
    override init(frame: CGRect){
        super.init(frame: frame)
        setupSubviews()
    }

    func setupSubviews(){
        self.translatesAutoresizingMaskIntoConstraints = false // This is the problematic line

        // ... some code to create subviews
   
        setupConstraints()
    }

    func setupConstraints(){
        NSLayoutConstraint.activate([
            // Adding the two lines of code below will correct the problem, although
            // it is probably better to remove the problematic line
            //self.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width)
            //self.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height)

            // ... constraints on subviews
        ])
    }
}

class PresentedSubClass1 : PresentedBaseClass{
    // ...
}

class PresentedSubClass2 : PresentedBaseClass{
    // ...
}

class PresentingClass : UIView{
var windowFrame: CGRect
   
    init(windowframe: CGRect){
        windowFrame = windowframe
        super.init(nibName: nil, bundle: nil)
    }

    @objc func originalPresentation(_ sender: UIButton){
        childVC = PresentedSubClass1Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        // The two lines below are the original workaround
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }

    @objc func gotoSubClass1(){
        childVC?.dismiss(animated: false, completion: nil)
        childVC = PresentedSubClass1Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }
   
    @objc func gotoSubClass2(){
        childVC?.dismiss(animated: false, completion: nil)
        childVC = PresentedSubClass2Controller(windowframe: windowFrame)
        present(childVC!, animated: false, completion: nil)
        //let appDelegate = UIApplication.shared.delegate
        //appDelegate?.window??.rootViewController = childVC
    }

}

class AppDelegate: UIResponder, UIApplicationDelegate{
    var window: UIWindow?
    var PresentingController: PresentingController!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        window = UIWindow(frame: UIScreen.main.bounds)
       
        PresentingController = PresentingController(windowframe: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
       
        window?.rootViewController = PresentingController
        window?.makeKeyAndVisible()
        return true
    }
}

Apparently, the problem was that the presented view was not properly constrained; its subviews were, but it wasn't. See code above.