Search code examples
iosswift3uiviewcontrollerchildviewcontroller

App crashes when removing child view controller from its parent


I'm trying to test some animations between UIViewControllers and in this particular case I have a UIViewController that adds another UIVC as its child view.

Everything works as expected, the child view gets added and presented, then on the child view I have a UINavigationBar which has a cancel button (dismiss) as its left bar button.

When I click on that button, I fire a function which tries to remove this presented child view, from the view hierarchy (from its parent view).

Code from the parent view:

    // ViewController -> Parent

lazy var presentButton: UIButton = {
    let b = UIButton(type: .custom)
    b.setTitle("Present", for: .normal)
    b.setTitleColor(.black, for: .normal)
    b.addTarget(self, action: #selector(didTapPresentButton), for: .touchUpInside)
    return b
}()

lazy var childViewController: PresentedViewController = {
    let viewController = PresentedViewController()
    return viewController
}()


@objc func didTapPresentButton() {
    addViewControllerAsChildViewController(childViewController: childViewController)    
}


func addViewControllerAsChildViewController(childViewController: UIViewController) {
    self.addChildViewController(childViewController)
    childViewController.view.frame = CGRect.zero

    self.view.addSubview(childViewController.view)

    let newFrame = view.bounds
    UIView.animate(withDuration: 2) {
        childViewController.view.frame = newFrame
    }
    childViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    childViewController.didMove(toParentViewController: self)
}

As you see above, when I click the present button, it instantiates the child view and animates it in, so far so good.

Child View code:

// ChildViewController -> Child (ofc)

@objc func didTapCancel() {
    self.willMove(toParentViewController: nil)
    self.view.removeFromSuperview()
    self.removeFromParentViewController()
}

Now on the child view, when I click the cancel button, I know I have to call the removeFromParentViewController() in order to it be properly removed, but the app crashes if I do that with the followingg error:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[dismissLayerTest.ChildViewController name]: unrecognized selector sent to instance 0x7fea2f60a920'

Then I tried to comment the self.removeFromParentViewController() line, and by doing so, the app doesn't crash, but then on the parent view controller I can see that the view is still attached to its parent by printing self.childViewControllers.count and it shows me 1.

Can you see where the problem is ?

Thanks


Solution

  • I figure out where the problem was.

    After some tests, I discovered that we should not use lazy instantiations while using parent-child View Controllers.

    Since the I instantiated the childViewController as lazy when I tried to remove it from the parentViewController the error said unrecognized selector sent to instance so I found out that the property pointer-selector was somehow deallocated on the parentViewController which didn't knew what child it was to dismiss, because it lost its reference.

    In order to fix it, I removed the lazy instantiation, so it remains always in scope and now I can successfully remove the child from its parent scope.

    var childViewController: ChildViewController = {
        let viewController = ChildViewController()
        return viewController
    }()