Why is superview returning nil?

At the very bottom is my code where MainController initiates a subview called setController. The new subview is created when I click the button

However, within setController's code I get back a nil when I try to the superview:

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("\(self.view.superview)" ?? "count->no parent")
    }

I am assigning the second view as a subview, but obviously I am missing something. Have I misunderstood how UIView hierarchy works?

class MainController: UIViewController {
    
    private lazy var setController = SetController()
    var invButton   : MyButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        invButton = makeButton(vControl: self, btype: ButtType.inv, action: #selector(self.buttonAction(sender:)))
        invButton.frame.origin.x = self.view.frame.width * 0.1
        invButton.frame.origin.y = self.view.frame.height * 0.1
        invButton.setTitle("Settings", for: .normal)
    }

    override var prefersStatusBarHidden: Bool {
        return false
    }
    
    @objc func buttonAction(sender: UIButton!) {
        guard let theButton = sender as? MyButton else { return}

        UIView.transition(with: self.view, duration: 0.5, options: .transitionCurlDown, animations: { [self] in
         self.addChild(setController)
        self.view.addSubview(setController.view)
        setController.didMove(toParent: self)
        }, completion: nil)
    }
}

Accepted Answer

See the Apple document excerpt below. If calling animation didMove(toParent:) is called after the transition not inside of the animation block, no animation the immediately after the addChild.

With animation

        @objc func buttonAction(sender: UIButton!) {
        guard let theButton = sender as? MyButton else { return}

        UIView.transition(with: self.view, duration: 0.5, options: .transitionCurlDown, animations: { [self] in
         self.addChild(setController)
        self.view.addSubview(setController.view)
        }, completion: {
        setController.didMove(toParent: self)
})
    }

No animation

        @objc func buttonAction(sender: UIButton!) {
        guard let theButton = sender as? MyButton else { return}

         self.view.addSubview(setController.view)   
         self.addChild(setController)
         setController.didMove(toParent: self)
  
    }

Discussion Your view controller can override this method when it wants to react to being added to a container. If you are implementing your own container view controller, it must call the didMove(toParent:) method of the child view controller after the transition to the new controller is complete or, if there is no transition, immediately after calling the addChild(_:) method. The removeFromParent() method automatically calls the didMove(toParent:) method of the child view controller after it removes the child.

Sundel: https://www.swiftbysundell.com/basics/child-view-controllers/

  • deleting my comment....thank you, that was it

Add a Comment

Answers

See the Apple document excerpt below. If calling animation didMove(toParent:) is called after the transition not inside of the animation block, no animation the immediately after the addChild.

With animation

        @objc func buttonAction(sender: UIButton!) {
        guard let theButton = sender as? MyButton else { return}

        UIView.transition(with: self.view, duration: 0.5, options: .transitionCurlDown, animations: { [self] in
         self.addChild(setController)
        self.view.addSubview(setController.view)
        }, completion: {
        setController.didMove(toParent: self)
})
    }

No animation

        @objc func buttonAction(sender: UIButton!) {
        guard let theButton = sender as? MyButton else { return}

         self.view.addSubview(setController.view)   
         self.addChild(setController)
         setController.didMove(toParent: self)
  
    }

Discussion Your view controller can override this method when it wants to react to being added to a container. If you are implementing your own container view controller, it must call the didMove(toParent:) method of the child view controller after the transition to the new controller is complete or, if there is no transition, immediately after calling the addChild(_:) method. The removeFromParent() method automatically calls the didMove(toParent:) method of the child view controller after it removes the child.

Sundel: https://www.swiftbysundell.com/basics/child-view-controllers/

  • deleting my comment....thank you, that was it

Add a Comment