UINavigationController正确添加子View Controller

问题产生

最近,在集成一个全屏广告页的时候,遇到视图控制器的生命周期异常的问题:除了viewDidLoad,其余函数都没有执行。广告页本身是一个 AdsVC,当某个页面需要展示全屏广告的时候,将自己作为参数传递给 AdsVC。在 AdsVC 中,会把调用者作为一个 container controller,进行页面的添加。
对于普通的 view controller,这样处理没有问题。但是,当如 UINavigationController 的 VC 调用的时候,会发生 AdsVC 的生命周期异常的问题。

原因和解决

基本确定 UINavigationController 作为 iOS 原生的 container controller,对 child controller 的添加/移除有内部的实现,从而导致 appear 相关的函数没有执行。
通过查看头文件,可以发现:

Discussion
If you are implementing a custom container controller, use this method to tell the child that its views are about to appear or disappear. Do not invoke viewWillAppear(_:), viewWillDisappear(_:), viewDidAppear(_:), or viewDidDisappear(_:) directly.

func beginAppearanceTransition(_ isAppearing: Bool, animated: Bool)

于是,解决方法就是调用 AppearanceTransition 相关的函数,如下:

open func showIn(_ viewController: UIViewController){
        viewController.addChild(self)
        self.view.frame = viewController.view.bounds
        if viewController is UINavigationController {
            self.beginAppearanceTransition(true, animated: true)
            viewController.view.addSubview(self.view)
            self.view.alpha = 0
            UIView.animate(withDuration: 0.2, animations: {
                self.view.alpha = 1
            }) { (_) in
                self.endAppearanceTransition()
                self.didMove(toParent: viewController)
            }
        }else {
            viewController.view.addSubview(self.view)
            self.didMove(toParent: viewController)
        }  
    }
    open func removeFrom(_ viewController: UIViewController){
        if viewController.view.subviews.contains(self.view) {
            if viewController is UINavigationController {
                self.willMove(toParent: nil)
                self.beginAppearanceTransition(false, animated: true)
                UIView.animate(withDuration: 0.2, animations: {
                    self.view.alpha = 0
                }) { (_) in
                    self.view.removeFromSuperview()
                    self.endAppearanceTransition()
                    self.removeFromParent()
                }
            }else {
                self.view.removeFromSuperview()
                self.removeFromParent()
            }
        }
    }