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 #1: 0x2c8f437e UIKit`-[UIViewController _setViewAppearState:isAnimating:] + 438
frame #2: 0x2c8e5a3c UIKit`-[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 572
frame #3: 0x2c8e4f22 UIKit`-[UIView(Internal) _addSubview:positioned:relativeTo:] + 394
frame #4: 0x2c8e4d8e UIKit`-[UIView(Hierarchy) addSubview:] + 30
frame #5: 0x0003c628 viewwithcontroller`-[ViewController setUp:](self=0x1554a5a0, _cmd=0x31c5450e, sender=0x156518c0) + 184 at ViewController.m:37
frame #6: 0x2c90fa7a UIKit`-[UIApplication sendAction:to:from:forEvent:] + 70
frame #7: 0x2c90fa20 UIKit`-[UIControl sendAction:to:forEvent:] + 44
frame #8: 0x2c8fa5c4 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 584
frame #9: 0x2c90f460 UIKit`-[UIControl touchesEnded:withEvent:] + 584
frame #10: 0x2c90f132 UIKit`-[UIWindow _sendTouchesForEvent:] + 522
frame #11: 0x2c908a40 UIKit`-[UIWindow sendEvent:] + 540
frame #12: 0x2c8dec04 UIKit`-[UIApplication sendEvent:] + 196
frame #13: 0x2cb5531e UIKit`_UIApplicationHandleEventFromQueueEvent + 14538


frame #0: 0x0003cab0 viewwithcontroller`-[BViewController viewDidAppear:](self=0x1554f940, _cmd=0x2d0b1ecb, animated='\0') + 24 at BViewController.m:32
frame #1: 0x2c8f43be UIKit`-[UIViewController _setViewAppearState:isAnimating:] + 502
frame #2: 0x2c959772 UIKit`-[UIViewController _executeAfterAppearanceBlock] + 58
frame #3: 0x2c9596f8 UIKit`_applyBlockToCFArrayCopiedToStack + 308

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 #17: 0x29267cc0 CoreFoundation`__CFRunLoopRun + 768
frame #18: 0x291b39a0 CoreFoundation`CFRunLoopRunSpecific + 476
frame #19: 0x291b37b2 CoreFoundation`CFRunLoopRunInMode + 106
frame #20: 0x3098c1a8 GraphicsServices`GSEventRunModal + 136
frame #21: 0x2c93e694 UIKit`UIApplicationMain + 1440
frame #22: 0x0003ccf0 viewwithcontroller`main(argc=1, argv=0x00149ac4) + 108 at main.m:14

frame #6: 0x29267950 CoreFoundation`__CFRunLoopDoObservers + 276
frame #7: 0x29267d52 CoreFoundation`__CFRunLoopRun + 914
frame #8: 0x291b39a0 CoreFoundation`CFRunLoopRunSpecific + 476
frame #9: 0x291b37b2 CoreFoundation`CFRunLoopRunInMode + 106
frame #10: 0x3098c1a8 GraphicsServices`GSEventRunModal + 136
frame #11: 0x2c93e694 UIKit`UIApplicationMain + 1440
frame #12: 0x0003ccf0 viewwithcontroller`main(argc=1, argv=0x00149ac4) + 108 at main.m:14

貌似,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。

posted @ 2015-08-03 21:42  Dalink  阅读(477)  评论(0编辑  收藏  举报