addChildViewController:给控制器添加子控制器
0.4 2018.04.29 16:17 字数 211 阅读 2318评论 4喜欢 3
用处:标签展示 () 等
好处:
1.无疑,对页面中的逻辑更加分明了。相应的View对应相应的ViewController。
2.当某个子View没有显示时,将不会被Load,减少了内存的使用。
3.当内存紧张时,没有Load的View将被首先释放,优化了程序的内存释放机制
使用步骤:
1.addChildViewController:的同时调用addSubView:
[selfaddChildViewController:sfViewControllr];[self.view addSubview:sfViewControllr.view];
2.设置子视图的位置,并显示出来
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中发通知,通知父Controller,移除子Controller
学习笔记:addChildViewController
0.1 2018.11.28 11:55* 字数 1098 阅读 193评论 0喜欢 1
说来惭愧,苹果在ios5就提供了addChildViewController这一方法,而我却一直没有使用过,也对它不甚了解,这次有机会用用,仔细学习下。
方法定义
swift中,对应的方法是:
/*
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已经有父VC了,在调用这个方法之前会先从父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.
大意是:
这一方法在 当前的VC 和 被添加的VC 之间创建了一种父-子的关系。当把一个VC的view添加到当前VC的view上的时候,就必须先使用addChild这个方法添加这种父子关系。如果子VC已经有父VC了,那么调用该方法会先把子VC从之前的父VC中移除,然后再添加到当前的VC上作为子VC。
这个方法只会在自定义的VC中被调用,如果打算重写这个方法,则必须调用super的此方法。
也就是说,苹果iOS5之后的API增加的 addChild(_ childController: UIViewController) 方法,并且希望我们在使用addSubview时,同时调用addChild(vc)方法将sub view对应的viewController也加到当前ViewController的管理中。
对于那些当前暂时不需要显示的subview,只通过addChild(_ childController: UIViewController)把subViewController加进去;需要显示时再调用transitionFromViewController方法。将其添加进入底层的ViewController中。
这样做的好处:
1.无疑,对页面中的逻辑更加分明了。相应的View对应相应的ViewController。
2.当某个子View没有显示时,将不会被Load,减少了内存的使用。
3.当内存紧张时,没有Load的View将被首先释放,优化了程序的内存释放机制。
在iOS5中,ViewController中新添加了下面几个方法:
addChildViewController:
removeFromParentViewController
transitionFromViewController:toViewController:duration:options:animations:completion:
willMoveToParentViewController:
didMoveToParentViewController:
使用场景:
我们的日常开发中常有这样一种需求,通过切换标签来切换不同的页面,如果在一个 controller 管理这些 view 的话,代码就显得耦合度过高,也与 Apple 的 MVC 模式不符合,Apple 推荐一个 controller 管理一个 view。 同样有人建议类似 [self.view addSubview:viewController.view] 的方式用另一个 controller 来控制,但是这样会产生一个新的问题:直接 add 进去的subView不在 viewController的 view hierarchy 内,事件不会正常传递,如:旋转、触摸等,属于危险操作违背CocoaTouch开发的设计MVC原则,viewController应该且只应该管理一个view hierarchy。同样我们也要考虑另一个问题:这些子 view 大多数不会一直处于界面上,只是在某些情况下才会出现,例如登陆失败的提示 view,上传附件成功的提示 view,网络失败的提示 view 等。但是虽然这些 view 很少出现,但是我们却常常一直把它们放在内存中。
关于子VC的生命周期:
当childViewController没有被加到任何父视图控制器时,如果把childViewController的view加到别的视图上,viewWillAppear和viewDidAppear会正常调用。但是当childViewController被加到一个父视图控制器上后,viewWillAppear和viewDidAppear就会与父视图控制器的viewWillAppear和viewDidAppear事件同步。
所以调用先后问题要注意:
先调用addSubView,viewWillAppear和viewDidAppear会各调用一次,再addChildViewController,与父视图控制器的事件同步,即当父视图控制器的viewDidAppear调用时,childViewController的viewDidAppear方法会再调用一次。所以viewDidAppear方法被调用了两次。
先调用addChildViewController,childViewController的事件与父视图控制器同步,当父视图控制器的viewDidAppear调用时,childViewController的viewDidAppear方法会调用一次,再调用addSubView也不会触发viewWillAppear和viewDidAppear。
iOS AddChildViewController遇到的问题
0.6 2019.01.10 16:40* 字数 578 阅读 1028评论 0喜欢 4
今天在项目中检查循环引用问题的时候遇到了此问题,查询了别的页面,发现打印log并不一样。所以查阅了资料,这里记录一下。
很多时候,我们都会遇到,在一个viewController中,添加别的controller,已达到特殊的转场效果,或者为了用户能在一个页面看到并和多个页面的内容交互的效果。
因此,苹果给我们提供了这个概念,和实现方法:addChildViewController
这里有一个使用场景,
页面初始化的时候,在viewDidLoad中添加了子控制器,此时先加载缓存数据(也就是页面会先展示)
然后展示的同时,重新调用接口,拉取数据。
得到数据之后,重新刷新页面
相信大部分人也都是这么实现的。
因此,demo中是这么写的:
A(ViewController)页面中有一个按钮,跳转到B(TestViewController)页面, 然后B页面中有两个按钮,第一个按钮用来第一次添加childViewController->C页面(SecViewController), 第二个页面,用来模拟网络请求得到结果之后,重新刷新布局的操作。
这里有一个上述操作打印的log图
可以看到中间很醒目的dealloc!此处就造成了我的疑惑
然后来看下containerViewController中是怎么实现的:
//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的时候调用。
可以看到;其中有一句
[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.
大致意思就是:
如果想要移除一个子控制器,需要自控制器调用willMoveToParentViewController, 移除子控制器view在父视图中的位置约束,从父容器的层级中移除子控制器,然后调用子控制器的removeFromParentViewController。
此种操作,就会造成子控制器不再被引用,然后执行了dealloc方法!
所以,此处就会造成, 在进入一个页面的时候(默认是只在dealloc处打印了,没有在viewDidLoad处打印),先看到了子控制器的dealloc
附 Demo
网友评论