iOS Child Controller

最近碰到一個問題是發(fā)現(xiàn)當(dāng)移除一個subview的時候,viewWillDisappear被調(diào)用,但是viewDidDisappear卻沒有被調(diào)用,導(dǎo)致注冊的通知沒有被注銷,進(jìn)而引發(fā)一系列的錯誤調(diào)用。趁此機會理清了一下有關(guān)"Child View Controller"的一些概念以及正確的調(diào)用方法。

Child View Controller

官方手冊UIViewControler定義了 "View Controller"的主要職責(zé),包括:更新視圖內(nèi)容;響應(yīng)用戶操作;調(diào)整視圖尺寸以及控制頁面布局。

對于我們這個Case中使用孩子視圖控制器的行為在手冊中叫做"Container View Controller",定義為:

A container view controller manages the presentation of content of other view controllers it owns, also known as its child view controllers

A child'??s view can be presented as-is or in conjunction with views owned by the container view controller.

引入addChildViewController事實上是為了更好的管理addSubView: 使得ViewViewController可以一一對應(yīng),可以使用多Controller從而避免混在一起,可以在暫時不需要顯示視圖的時候不載入,而等到需要的時候再在家,并且通過這個還可以更加方便的進(jìn)行視圖切換,減少代碼耦合。(于此對應(yīng),iOS5之前,即使不顯示,所有的view也都被加載在內(nèi)存中)

ViewController容器篇這篇文章的總結(jié)蠻好,介紹了addChildViewController相關(guān)的API的使用規(guī)則:

//添加
[self addChildViewController: _currentVC];
//[_currentVC willMoveToParentViewController: self];(自動調(diào)用  省略)
//[_currentVC didMoveToParentViewController: self]; (可省略)

//移除
[_currentVC willMoveToParentViewController: nil];
[_currentVC removeFromParentViewController];
//[_currentVC didMoveToParentViewController: nil]; (自動調(diào)用 省略)

//轉(zhuǎn)換
[_currentVC willMoveToParentViewController: nil];
[self transitionFromViewController: _currentVC toViewController: _secondVC];
[_secondVC didMoveToParentViewController: self];

//轉(zhuǎn)換子視圖控制器
- (void)transitionFromOldViewController:(UIViewController *)oldViewController toNewViewController:(UIViewController *)newViewController{
    [self transitionFromViewController:oldViewController toViewController:newViewController duration:0.3 options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
        if (finished) {
            [newViewController didMoveToParentViewController:self];
            _currentVC = newViewController;
        }else{
            _currentVC = oldViewController;
        }
    }];
}

使用轉(zhuǎn)換時,不需要移除"Child View Controller",保持多個"Child View Controller",并在之間切換是這個功能的目的之一。需要注意的是如何使用"MoveTo",而這里有一個重要的益處就是不需要再手動管理addSubView僅需在第一次初始化時使用,后面轉(zhuǎn)換時不需要。

為了檢測這些方法和孩子視圖控制器之間的調(diào)用映射,寫了下面的代碼進(jìn)行了測試:

var viewControllerOne:TestOneViewController? = TestOneViewController()
    
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
        
    NotificationCenter.default.addObserver(self, selector: #selector(clickAction), name:NSNotification.Name.UIApplicationWillResignActive, object: nil)
        
    if let currentViewController = viewControllerOne {
        self.addChildViewController(currentViewController)//加入self.childViewControllers, 不導(dǎo)致調(diào)用
        self.view.addSubview(currentViewController.view)//導(dǎo)致調(diào)用 viewWillAppear/viewDidAppear
        currentViewController.didMove(toParentViewController: self)
    }
}
    
func clickAction() {
    if let currentViewController = viewControllerOne {
        currentViewController.willMove(toParentViewController: nil)
        currentViewController.view.removeFromSuperview()//導(dǎo)致調(diào)用 viewWillDisappear/viewDidDisappear
        currentViewController.removeFromParentViewController()//移出self.childViewControllers, 不導(dǎo)致調(diào)用
        viewControllerOne = nil//導(dǎo)致調(diào)用 deinit
    }
}

可以總結(jié)說:添加或者刪除孩子視圖控制器的行為不會導(dǎo)致孩子視圖控制器的Action,而添加或者刪除視圖會直接導(dǎo)致相應(yīng)的Action,而要保證孩子視圖占用內(nèi)存被及時釋放,需要顯示注銷任何的引用。

Navigation Controller

UINavigationController事實上就是一個"Container View Controller",而它提供了"Push/Pop"兩個方法來方便的添加和移除"Child View Controller",可以直接翻譯成上面的添加移除代碼。

在"popViewController"模式下,是無法將最底層的"View Controller"也移除的,也就是至少保留一個:


如果希望去除所有的"Child View Controller",可以通過重置viewControllers實現(xiàn),另外一點需要注意的是,在這種情況下,僅僅最上層的那個會調(diào)用"viewWillDisappear/viewDidDisappear":

從這里也會發(fā)現(xiàn),我們是不需要手動調(diào)用removeFromSuperview的,這些可以自動完成。

引用

Apple UIViewControler

ViewController容器篇

iOS addChildViewController方法

Custom Container View Controller

唐巧iOS5中UIViewController的新方法

How to remove UIViewControllers from stack so ViewDidDisappear is called?

iOS UINavigationController里的push和pop操作、抽屜效果原理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容