0

I'm trying to add a child view controller to a parent controller using an extension to UIViewController. This works when I don't use the extension by calling the following to add the child controller:

addChild(child)
child.view.frame = view.frame
view.addSubview(child.view)
child.didMove(toParent: self)

And the following to remove the child view controller:

child.willMove(toParent: nil)
child.view.removeFromSuperview()
child.removeFromParent()

The expected result is that when the button is tapped the child view controller is added, the spinner view controller loads, UIActivityIndicatorView spins for two seconds and then the child view controller is removed. This works as expected when the extension is not used. There are no error messages. When using the extension the spinner does not appear - nothing happens.

SpinnerViewController.swift (not associated with a view controller scene in Interface Builder):

import UIKit    

class SpinnerViewController: UIViewController {
        var spinner = UIActivityIndicatorView(style: .whiteLarge)
    
        override func loadView() {
            view = UIView()
            view.backgroundColor = UIColor(white: 0, alpha: 0.7)
    
            spinner.translatesAutoresizingMaskIntoConstraints = false
            spinner.startAnimating()
            view.addSubview(spinner)
    
            spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
            spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        }
    }

Here is ViewController.swift which is associated with a view controller scene in Interface Builder:

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
        }
        
        @IBAction func pressToSpinButtonTapped(_ sender: UIButton) {
            createSpinnerView()
        }
        
        func createSpinnerView() {
            let child = SpinnerViewController()
    
            // add the spinner view controller
    //        addChild(child)
    //        child.view.frame = view.frame
    //        view.addSubview(child.view)
    //        child.didMove(toParent: self)
            self.add(child)
    
            // wait two seconds to simulate some work happening
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                // then remove the spinner view controller
    //            child.willMove(toParent: nil)
    //            child.view.removeFromSuperview()
    //            child.removeFromParent()
                self.remove()
            }
        }
    
    }

@nonobjc extension UIViewController {
    func add(_ child: UIViewController, frame: CGRect? = nil) {
        addChild(child)

        if let frame = frame {
            child.view.frame = frame
        }

        view.addSubview(child.view)
        child.didMove(toParent: self)
    }

    func remove() {
        guard parent != nil else { return }
        
        willMove(toParent: nil)
        view.removeFromSuperview()
        removeFromParent()
    }
}

1 Answer 1

3

You need to update your method like this:

func add(_ child: UIViewController, frame: CGRect? = nil) {
        addChild(child)
        //child.view.frame = self.view.frame
        if frame != nil {
          if let frame = frame {
              child.view.frame = frame
          }
        } else {
          child.view.frame = self.view.frame
        }
        view.addSubview(child.view)
        child.didMove(toParent: self)
    }

func remove(_ child: UIViewController) {
    child.view.removeFromSuperview()
    willMove(toParent: nil)
    removeFromParent()
}
Sign up to request clarification or add additional context in comments.

2 Comments

Could this be improved by using a nil-coalescing operation on the optional frame parameter? child.view.frame = frame ?? self.view.frame
yes you can update it like: func add(_ child: UIViewController, frame: CGRect? = nil) { if frame != nil { if let frame = frame { child.view.frame = frame } } else { child.view.frame = self.view.frame } addChild(child) view.addSubview(child.view) child.didMove(toParent: self) }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.