前言
控制器容器Container的主要職責(zé)就是管理一個(gè)或多個(gè)Child View Controller的展示的生命周期夷狰,需要傳遞顯示以及旋轉(zhuǎn)相關(guān)的回調(diào)。能夠有效的分離業(yè)務(wù)邏輯、減輕一些復(fù)雜控制器的復(fù)雜度,有利于代碼的理解與維護(hù)。
相關(guān)知識(shí)
顯示或者旋轉(zhuǎn)的回調(diào)的觸發(fā)的源頭來(lái)自于window,一個(gè)app首先有一個(gè)主window帮毁,初始化的時(shí)候需要給這個(gè)主window指定一個(gè)rootViewController,window會(huì)將顯示相關(guān)的回調(diào)(viewWillAppear:, viewWillDisappear:, viewDidAppear:, or viewDidDisappear: )以及旋轉(zhuǎn)相關(guān)的回調(diào)(willRotateToInterfaceOrientation:duration: ,willAnimateRotationToInterfaceOrientation:duration:, didRotateFromInterfaceOrientation:)傳遞給rootViewController豺撑。rootViewController需要再將這些callbacks的調(diào)用傳遞給它的Child View Controllers烈疚。
展示子控制器
[self addChildViewController:content]; //1
content.view.frame = [self frameForContentController];
[self.view addSubview:self.currentClientView]; //2
[content didMoveToParentViewController:self]; //3
1.將content添加為child view controller,addChildViewController:接口建立了邏輯上的父子關(guān)系聪轿,子可以通過(guò)parentViewController爷肝,訪問(wèn)其父VC,addChildViewController:接口的邏輯中會(huì)自動(dòng)調(diào)用 [content willMoveToParentViewController:self];
2.建立父子關(guān)系后陆错,便是將content的view加入到父VC的view hierarchy上灯抛,同時(shí)要決定的是 content的view顯示的區(qū)域范圍。
3.調(diào)用child的 didMoveToParentViewController: 音瓷,以通知child对嚼,完成了父子關(guān)系的建立
移除子控制器
[content willMoveToParentViewController:nil]; //1
[content.view removeFromSuperview]; //2
[content removeFromParentViewController]; //3
1.通知child,即將解除父子關(guān)系绳慎,從語(yǔ)義上也可以看出 child的parent即將為nil
2.將child的view從父VC的view的hierarchy中移除
3.通過(guò)removeFromParentViewController的調(diào)用真正的解除關(guān)系纵竖,removeFromParentViewController會(huì)自動(dòng)調(diào)用 [content didMoveToParentViewController:nil]
appearance callbacks的傳遞
上面的實(shí)現(xiàn)中有一個(gè)問(wèn)題,就是沒(méi)看到那些appearance callbacks是如何傳遞的杏愤,答案就是appearance callbacks默認(rèn)情況下是自動(dòng)調(diào)用的靡砌,蘋(píng)果框架底層幫你實(shí)現(xiàn)好了,也就是在上面的addSubview的時(shí)候珊楼,在subview真正加到父view之前通殃,child的viewWillAppear將被調(diào)用,真正被add到父view之后厕宗,viewDidAppear會(huì)被調(diào)用画舌。移除的過(guò)程中viewWillDisappear堕担,viewDidDisappear的調(diào)用過(guò)程也是類(lèi)似的。
有時(shí)候自動(dòng)的appearance callbacks的調(diào)用并不能滿足需求骗炉,比如child view的展示有一個(gè)動(dòng)畫(huà)的過(guò)程,這個(gè)時(shí)候我們并不想viewDidAppear的調(diào)用在addSubview的時(shí)候進(jìn)行蛇受,而是等展示動(dòng)畫(huà)結(jié)束后再調(diào)用viewDidAppear句葵。
也許你可能會(huì)提到 transitionFromViewController:toViewController:duration:options:animations:completion: 這個(gè)方法,會(huì)幫你自動(dòng)處理view的add和remove兢仰,以及支持animations block乍丈,也能夠保證在動(dòng)畫(huà)開(kāi)始前調(diào)用willAppear或者willDisappear,在調(diào)用結(jié)束的時(shí)候調(diào)用didAppear把将,didDisappear轻专,但是此方式也存在局限性,必須是兩個(gè)新老子VC的切換察蹲,都不能為空请垛,因?yàn)橐WC新老VC擁有同一個(gè)parentViewController,且參數(shù)中的viewController不能是系統(tǒng)中的container洽议,比如不能是UINavigationController或者UITabbarController等宗收。
有時(shí)候并不想使用默認(rèn)情況下的自動(dòng)調(diào)用,那么shouldAutomaticallyForwardAppearanceMethods方法并返回NO就可以禁止系統(tǒng)的默認(rèn)調(diào)用亚兄。
手動(dòng)傳遞的時(shí)候你并不能直接去調(diào)用child 的viewWillAppear或者viewDidAppear這些方法混稽,而是需要使用 beginAppearanceTransition:animated:和endAppearanceTransition接口來(lái)間接觸發(fā)那些appearance callbacks,且begin和end必須成對(duì)出現(xiàn)审胚。[content beginAppearanceTransition:YES animated:animated]觸發(fā)content的viewWillAppear匈勋,[content beginAppearanceTransition:NO animated:animated]觸發(fā)content的viewWillDisappear,和他們配套的[content endAppearanceTransition]分別觸發(fā)viewDidAppear和viewDidDisappear膳叨。
rotation callbacks的傳遞
也許在iPhone上很少要關(guān)心的屏幕旋轉(zhuǎn)問(wèn)題的洽洁,但是大屏幕的iPad上就不同了,很多時(shí)候你需要關(guān)心橫豎屏菲嘴。rotation callbacks 一般情況下只需要關(guān)心三個(gè)方法 willRotateToInterfaceOrientation:duration:在旋轉(zhuǎn)開(kāi)始前诡挂,此方法會(huì)被調(diào)用;willAnimateRotationToInterfaceOrientation:duration: 此方法的調(diào)用在旋轉(zhuǎn)動(dòng)畫(huà)block的內(nèi)部临谱,也就是說(shuō)在此方法中的代碼會(huì)作為旋轉(zhuǎn)animation block的一部分璃俗;didRotateFromInterfaceOrientation:此方法會(huì)在旋轉(zhuǎn)結(jié)束時(shí)被調(diào)用。而作為view controller container 就要肩負(fù)起旋轉(zhuǎn)的決策以及旋轉(zhuǎn)的callbacks的傳遞的責(zé)任悉默。
禁掉默認(rèn)調(diào)用需要重寫(xiě)兩個(gè)方法 shouldAutorotate和supportedInterfaceOrientations,前者決定再旋轉(zhuǎn)的時(shí)候是否去根據(jù)supportedInterfaceOrientations所支持的取向來(lái)決定是否旋轉(zhuǎn)城豁,也就是說(shuō)如果shouldAutorotate返回YES的時(shí)候,才會(huì)去調(diào)用supportedInterfaceOrientations檢查當(dāng)前view controller支持的取向抄课,如果當(dāng)前取向在支持的范圍中唱星,則進(jìn)行旋轉(zhuǎn)雳旅,如果不在則不旋轉(zhuǎn);而當(dāng)shouldAutorotate返回NO的時(shí)候间聊,則根本不會(huì)去管supportedInterfaceOrientations這個(gè)方法攒盈,反正是不會(huì)跟著設(shè)備旋轉(zhuǎn)就是了。
作為控制器容器需要注意的就是你需要去檢查你的child view controller哎榴,檢查他們對(duì)橫豎屏的支持情況型豁,以便容器自己決策在橫豎屏旋轉(zhuǎn)時(shí)候是否支持當(dāng)前的取向,和上面的callbacks傳遞的方向相比尚蝌,這其實(shí)是一個(gè)反向的傳遞迎变。