UIView和UIViewController关系简析
一、在MFC、QT这些技术中,CWnd、QWidget单独存在用以展现程序界面。但是在iOS中,UIView往往与UIViewController成对出现,并且成为UIViewController的“附属”。不敢说这种设计有多高明之处,但是确确实实可以帮助程序开发人员理清楚视图和逻辑数据之间的关系。接下来就扒一扒UIView和UIViewController之间的关系。
二、UIViewController提供如下维护方法。
- (void)addChildViewController:(UIViewController * _Nonnull)childController //这个方法定义了父视图和子视图之间的关系。如果被添加的子视图控制器已经有了父视图控制器,它在被添加之前先背移除。
- (void)removeFromParentViewController //从它的父视图控制器中移除。
三、UIView的生命周期和UIViewController的生命周期之间的关系。
创建一个demo工程。
@implementation ViewController - (IBAction)initView:(id)sender { vc = [[BViewController alloc] init]; NSLog(@"initView1"); vc.view.frame = CGRectMake(100, 100, 100, 100); NSLog(@"initView2"); [self addChildViewController:vc]; NSLog(@"initView3"); } - (IBAction)setUp:(id)sender { NSLog(@"setUp1"); [self.view addSubview:vc.view]; NSLog(@"setUp2"); } - (IBAction)remove:(id)sender { NSLog(@"remove1"); [vc.view removeFromSuperview]; NSLog(@"remove2"); } - (IBAction)moveOut:(id)sender { NSLog(@"moveOut1"); [vc.view setFrame:CGRectMake(1000, 0, 100, 100)]; NSLog(@"moveOut2"); } - (IBAction)setChildViewHidden:(id)sender { NSLog(@"setChildViewHidden1"); vc.view.hidden = YES; NSLog(@"setChildViewHidden2"); } - (IBAction)cancelChildViewHidden:(id)sender { NSLog(@"cancelChildViewHidden1"); vc.view.hidden = NO; NSLog(@"cancelChildViewHidden2"); } @end
@implementation BViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor yellowColor]; NSLog(@"BViewController viewDidLoad"); } - (void)viewDidAppear:(BOOL)animated { NSLog(@"BViewController viewDidAppear"); } - (void)viewWillAppear:(BOOL)animated { NSLog(@"BViewController viewWillAppear"); } - (void)viewWillDisappear:(BOOL)animated { NSLog(@"BViewController viewWillDisappear"); } - (void)viewDidDisappear:(BOOL)animated { NSLog(@"BViewController viewDidDisappear"); } @end
点击按钮后,分别做子视图的移动、添加、删除、隐藏等操作。在这些方法中,都放置详细的log日志。用以查看调用关系。分别操作后,输出日志。
2015-08-03 19:45:44.185 viewwithcontroller[810:265324] initView1 2015-08-03 19:45:44.189 viewwithcontroller[810:265324] BViewController viewDidLoad 2015-08-03 19:45:44.189 viewwithcontroller[810:265324] initView2 2015-08-03 19:45:44.190 viewwithcontroller[810:265324] initView3 2015-08-03 19:45:46.527 viewwithcontroller[810:265324] setUp1 2015-08-03 19:45:46.528 viewwithcontroller[810:265324] BViewController viewWillAppear 2015-08-03 19:45:46.529 viewwithcontroller[810:265324] setUp2 2015-08-03 19:45:46.534 viewwithcontroller[810:265324] BViewController viewDidAppear 2015-08-03 19:45:50.071 viewwithcontroller[810:265324] remove1 2015-08-03 19:45:50.073 viewwithcontroller[810:265324] BViewController viewWillDisappear 2015-08-03 19:45:50.074 viewwithcontroller[810:265324] remove2 2015-08-03 19:45:50.077 viewwithcontroller[810:265324] BViewController viewDidDisappear 2015-08-03 19:45:52.974 viewwithcontroller[810:265324] setUp1 2015-08-03 19:45:52.976 viewwithcontroller[810:265324] BViewController viewWillAppear 2015-08-03 19:45:52.977 viewwithcontroller[810:265324] setUp2 2015-08-03 19:45:52.980 viewwithcontroller[810:265324] BViewController viewDidAppear 2015-08-03 19:45:54.896 viewwithcontroller[810:265324] setChildViewHidden1 2015-08-03 19:45:54.897 viewwithcontroller[810:265324] setChildViewHidden2 2015-08-03 19:45:58.652 viewwithcontroller[810:265324] cancelChildViewHidden1 2015-08-03 19:45:58.653 viewwithcontroller[810:265324] cancelChildViewHidden2 2015-08-03 19:46:01.602 viewwithcontroller[810:265324] moveOut1 2015-08-03 19:46:01.603 viewwithcontroller[810:265324] moveOut2
可以得出结论:
UIView | UIViewController |
访问view | viewDidLoad |
addSubView | viewWillApper、viewDidApper |
removeFromSuperView | viewWillDisapper、viewDidDisapper |
1、View的移除和创建才会影响ViewController的生命周期。
2、隐藏不会影响ViewController的生命周期。
3、创建view后,并不会创建对应的view,而是第一次访问后,做一次单例加载view,并执行ViewController的viewDidLoad方法。
四、更深入观察
当View被add进父View后,先后执行了viewwillAppear和viewDidAppear。到底是什么操作会分别影响viewwillAppear和viewDidAppear呢。在B中设置两个短点,分别查看调用栈,并对比。
viewWillAppear | viewDidAppear |
frame #0: 0x0003ca8c viewwithcontroller`-[BViewController viewWillAppear:](self=0x1554f940, _cmd=0x2d0b0814, animated='\0') + 24 at BViewController.m:28 |
|
frame #4: 0x2c8d3f52 UIKit`_afterCACommitHandler + 494 |
frame #14: 0x2c8dd606 UIKit`_UIApplicationHandleEventQueue + 1350 |
frame #15: 0x2926a22e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14 |
frame #5: 0x2926a26c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 20 |
frame #16: 0x29269642 CoreFoundation`__CFRunLoopDoSources0 + 222 |
frame #6: 0x29267950 CoreFoundation`__CFRunLoopDoObservers + 276 |
貌似,viewDidApper在执行[UIViewController _executeAfterAppearanceBlock] 之后,由RunLoop调用。
再次修改demo代码如下:
- (IBAction)initView:(id)sender { vc = [[BViewController alloc] init]; NSLog(@"initView1"); vc.view.frame = CGRectMake(100, 100, 100, 100); NSLog(@"initView2"); [self addChildViewController:vc]; NSLog(@"initView3"); helpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 200)]; helpView.backgroundColor = [UIColor greenColor]; [helpView addSubview:vc.view]; } - (IBAction)setUp:(id)sender { NSLog(@"setUp1"); [self.view addSubview:helpView]; dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"setUp2"); }); } - (IBAction)remove:(id)sender { NSLog(@"remove1"); [helpView removeFromSuperview]; NSLog(@"remove2"); }
输出日志为:
1 2015-08-03 21:35:10.763 viewwithcontroller[1036:18896] initView1 2 2015-08-03 21:35:10.764 viewwithcontroller[1036:18896] BViewController viewDidLoad 3 2015-08-03 21:35:10.764 viewwithcontroller[1036:18896] initView2 4 2015-08-03 21:35:10.764 viewwithcontroller[1036:18896] initView3 5 2015-08-03 21:35:11.563 viewwithcontroller[1036:18896] setUp1 6 2015-08-03 21:35:11.563 viewwithcontroller[1036:18896] BViewController viewWillAppear 7 2015-08-03 21:35:11.573 viewwithcontroller[1036:18896] setUp2 8 2015-08-03 21:35:11.573 viewwithcontroller[1036:18896] BViewController viewDidAppear 9 2015-08-03 21:35:12.702 viewwithcontroller[1036:18896] remove1 10 2015-08-03 21:35:12.703 viewwithcontroller[1036:18896] BViewController viewWillDisappear 11 2015-08-03 21:35:12.703 viewwithcontroller[1036:18896] remove2 12 2015-08-03 21:35:12.723 viewwithcontroller[1036:18896] BViewController viewDidDisappear
看来,addSubView触发viewWillApper,而视图渲染完成后才回触发viewDidApper。