addChildViewController:給控制器添加子控制器
?0.4?2018.04.29 16:17?字?jǐn)?shù) 211?閱讀 2318評論 4喜歡 3
用處:標(biāo)簽展示 () 等
好處:
1.無疑,對頁面中的邏輯更加分明了赛蔫。相應(yīng)的View對應(yīng)相應(yīng)的ViewController。
2.當(dāng)某個子View沒有顯示時减响,將不會被Load牙丽,減少了內(nèi)存的使用蹲坷。
3.當(dāng)內(nèi)存緊張時绿聘,沒有Load的View將被首先釋放爬凑,優(yōu)化了程序的內(nèi)存釋放機制
使用步驟:
1.addChildViewController:的同時調(diào)用addSubView:
[selfaddChildViewController:sfViewControllr];[self.view addSubview:sfViewControllr.view];
2.設(shè)置子視圖的位置级解,并顯示出來
sfViewControllr.view.frame =CGRectMake(0,300,1,1);[sfViewControllr didMoveToParentViewController:self];
3.移除子視圖
[sfViewControllr willMoveToParentViewController:nil];[sfViewControllr removeFromParentViewController];[sfViewControllr.view removeFromSuperview];
可能遇到的問題:
如果在子Controller中冒黑,把自己從父Controller中移除,在ios6中沒問題勤哗,在iOS7中抡爹,會崩潰
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController]; //ios7中崩潰
暫時的解決方法,在子Controller中發(fā)通知俺陋,通知父Controller豁延,移除子Controller
學(xué)習(xí)筆記:addChildViewController
?0.1?2018.11.28 11:55*?字?jǐn)?shù) 1098?閱讀 193評論 0喜歡 1
說來慚愧,蘋果在ios5就提供了addChildViewController這一方法腊状,而我卻一直沒有使用過诱咏,也對它不甚了解,這次有機會用用映屋,仔細學(xué)習(xí)下苟鸯。
方法定義
swift中早处,對應(yīng)的方法是:
/*
? ? ? If the child controller has a different parent controller, it will first be removed from its current parent
? ? ? by calling removeFromParentViewController. If this method is overridden then the super implementation must
? ? ? be called.
大意是:如果被添加的VC已經(jīng)有父VC了,在調(diào)用這個方法之前會先從父VC中移除桃序,然后再被添加到目前的VC上坟比,做為目前VC的子VC。
? ? */@available(iOS5.0, *)? ? open func addChild(_ childController: UIViewController)
蘋果對這一方法的解釋:
This method creates a parent-child relationship between the current view controller and the object in the childController parameter. This relationship is necessary when embedding the child view controller’s view into the current view controller’s content. If the new child view controller is already the child of a container view controller, it is removed from that container before being added.
This method is only intended to be called by an implementation of a custom container view controller. If you override this method, you must call super in your implementation.
大意是:
這一方法在 當(dāng)前的VC 和 被添加的VC 之間創(chuàng)建了一種父-子的關(guān)系巩割。當(dāng)把一個VC的view添加到當(dāng)前VC的view上的時候,就必須先使用addChild這個方法添加這種父子關(guān)系。如果子VC已經(jīng)有父VC了,那么調(diào)用該方法會先把子VC從之前的父VC中移除侥祭,然后再添加到當(dāng)前的VC上作為子VC胎署。
這個方法只會在自定義的VC中被調(diào)用恢筝,如果打算重寫這個方法抱究,則必須調(diào)用super的此方法。
也就是說敢靡,蘋果iOS5之后的API增加的 addChild(_ childController: UIViewController) 方法纺念,并且希望我們在使用addSubview時瑟蜈,同時調(diào)用addChild(vc)方法將sub view對應(yīng)的viewController也加到當(dāng)前ViewController的管理中夷都。
對于那些當(dāng)前暫時不需要顯示的subview,只通過addChild(_ childController: UIViewController)把subViewController加進去肝陪;需要顯示時再調(diào)用transitionFromViewController方法。將其添加進入底層的ViewController中刑顺。
這樣做的好處:
1.無疑,對頁面中的邏輯更加分明了狼讨。相應(yīng)的View對應(yīng)相應(yīng)的ViewController。
2.當(dāng)某個子View沒有顯示時衅檀,將不會被Load,減少了內(nèi)存的使用淘衙。
3.當(dāng)內(nèi)存緊張時,沒有Load的View將被首先釋放腻暮,優(yōu)化了程序的內(nèi)存釋放機制具垫。
在iOS5中,ViewController中新添加了下面幾個方法:
addChildViewController:
removeFromParentViewController
transitionFromViewController:toViewController:duration:options:animations:completion:
willMoveToParentViewController:
didMoveToParentViewController:
使用場景:
我們的日常開發(fā)中常有這樣一種需求试幽,通過切換標(biāo)簽來切換不同的頁面筝蚕,如果在一個 controller 管理這些 view 的話,代碼就顯得耦合度過高铺坞,也與 Apple 的 MVC 模式不符合起宽,Apple 推薦一個 controller 管理一個 view。 同樣有人建議類似 [self.view addSubview:viewController.view] 的方式用另一個 controller 來控制济榨,但是這樣會產(chǎn)生一個新的問題:直接 add 進去的subView不在 viewController的 view hierarchy 內(nèi)坯沪,事件不會正常傳遞,如:旋轉(zhuǎn)擒滑、觸摸等腐晾,屬于危險操作違背CocoaTouch開發(fā)的設(shè)計MVC原則叉弦,viewController應(yīng)該且只應(yīng)該管理一個view hierarchy。同樣我們也要考慮另一個問題:這些子 view 大多數(shù)不會一直處于界面上藻糖,只是在某些情況下才會出現(xiàn)淹冰,例如登陸失敗的提示 view,上傳附件成功的提示 view巨柒,網(wǎng)絡(luò)失敗的提示 view 等樱拴。但是雖然這些 view 很少出現(xiàn),但是我們卻常常一直把它們放在內(nèi)存中潘拱。
關(guān)于子VC的生命周期:
當(dāng)childViewController沒有被加到任何父視圖控制器時疹鳄,如果把childViewController的view加到別的視圖上,viewWillAppear和viewDidAppear會正常調(diào)用芦岂。但是當(dāng)childViewController被加到一個父視圖控制器上后瘪弓,viewWillAppear和viewDidAppear就會與父視圖控制器的viewWillAppear和viewDidAppear事件同步。
所以調(diào)用先后問題要注意:
先調(diào)用addSubView禽最,viewWillAppear和viewDidAppear會各調(diào)用一次腺怯,再addChildViewController,與父視圖控制器的事件同步川无,即當(dāng)父視圖控制器的viewDidAppear調(diào)用時呛占,childViewController的viewDidAppear方法會再調(diào)用一次。所以viewDidAppear方法被調(diào)用了兩次懦趋。
先調(diào)用addChildViewController晾虑,childViewController的事件與父視圖控制器同步,當(dāng)父視圖控制器的viewDidAppear調(diào)用時仅叫,childViewController的viewDidAppear方法會調(diào)用一次帜篇,再調(diào)用addSubView也不會觸發(fā)viewWillAppear和viewDidAppear。
iOS AddChildViewController遇到的問題
?0.6?2019.01.10 16:40*?字?jǐn)?shù) 578?閱讀 1028評論 0喜歡 4
今天在項目中檢查循環(huán)引用問題的時候遇到了此問題诫咱,查詢了別的頁面笙隙,發(fā)現(xiàn)打印log并不一樣。所以查閱了資料坎缭,這里記錄一下竟痰。
很多時候,我們都會遇到掏呼,在一個viewController中坏快,添加別的controller,已達到特殊的轉(zhuǎn)場效果憎夷,或者為了用戶能在一個頁面看到并和多個頁面的內(nèi)容交互的效果假消。
因此,蘋果給我們提供了這個概念岭接,和實現(xiàn)方法:addChildViewController
這里有一個使用場景富拗,
頁面初始化的時候臼予,在viewDidLoad中添加了子控制器,此時先加載緩存數(shù)據(jù)(也就是頁面會先展示)
然后展示的同時啃沪,重新調(diào)用接口粘拾,拉取數(shù)據(jù)。
得到數(shù)據(jù)之后创千,重新刷新頁面
相信大部分人也都是這么實現(xiàn)的缰雇。
因此,demo中是這么寫的:
A(ViewController)頁面中有一個按鈕追驴,跳轉(zhuǎn)到B(TestViewController)頁面, 然后B頁面中有兩個按鈕械哟,第一個按鈕用來第一次添加childViewController->C頁面(SecViewController), 第二個頁面,用來模擬網(wǎng)絡(luò)請求得到結(jié)果之后殿雪,重新刷新布局的操作暇咆。
這里有一個上述操作打印的log圖
可以看到中間很醒目的dealloc!此處就造成了我的疑惑
然后來看下containerViewController中是怎么實現(xiàn)的:
//TestViewController.m- (void)configureWithData:(NSArray*)array{if(!array.count)return;NSLog(@"~~~~~~~~~~~~~~~~~~~~%@ ---- %@ 前",NSStringFromClass([selfclass]),NSStringFromSelector(_cmd));? ? [self.childViewControllers makeObjectsPerformSelector:@selector(removeFromParentViewController)];NSLog(@"~~~~~~~~~~~~~~~~~~~~%@ ---- %@ 后",NSStringFromClass([selfclass]),NSStringFromSelector(_cmd));? ? ? ? [array enumerateObjectsUsingBlock:^(id_Nonnull obj,NSUIntegeridx,BOOL* _Nonnull stop) {? ? ? ? SecViewController *listVC = [[SecViewController alloc] init];? ? ? ? listVC.view.frame =self.view.bounds;? ? ? ? listVC.parentContainerViewController =self;? ? ? ? [selfaddChildViewController:listVC];//? ? ? ? [listVC didMoveToParentViewController:self];}];}
此方法在重新布局UI的時候調(diào)用丙曙。
可以看到爸业;其中有一句
[self.childViewControllers makeObjectsPerformSelector:@selector(removeFromParentViewController)];
此處記錄一下:
蘋果官方文檔中是這么描述的:
### Removing a Child View ControllerTo remove a child view controller from your content, remove theparent-child relationship between the view controllers by doing the following:1.Call the child’s `[willMoveToParentViewController:](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621381-willmovetoparentviewcontroller)` method with the value `nil`.2.Remove any constraints that you configured with the child’s root view.3.Remove the child’s root view from your container’s view hierarchy.4.Call the child’s `[removeFromParentViewController](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621425-removefromparentviewcontroller)` method to finalize the end of the parent-child relationship.Removing a child view controller permanently severs the relationship betweenparentandchild. Remove a child view controller only when you no longer need to refer to it.Forexample, a navigation controller does not remove its current child view controllers when anewone is pushed onto the navigation stack. It removes them only when they are popped off the stack.
大致意思就是:
如果想要移除一個子控制器,需要自控制器調(diào)用willMoveToParentViewController, 移除子控制器view在父視圖中的位置約束亏镰,從父容器的層級中移除子控制器扯旷,然后調(diào)用子控制器的removeFromParentViewController。
此種操作索抓,就會造成子控制器不再被引用钧忽,然后執(zhí)行了dealloc方法!
所以逼肯,此處就會造成惰瓜, 在進入一個頁面的時候(默認(rèn)是只在dealloc處打印了,沒有在viewDidLoad處打雍嚎蟆),先看到了子控制器的dealloc
附?Demo