自定义viewcontroller总结

一. UIViewController


UIViewController的View是lazy loading的,只有访问其view属性时,view才会加载(通过xib载入或者loadView方法中代码实现),可以通过UIViewController的isViewLoaded来判断其View是否已经加载。

当UIViewController的view将要被加入到view hierarchy中时,viewWillAppear会被调用,完成加入时,viewDidAppear会被调用。将view从 view hierarchy中移除时,分别调用viewWillDisappear/viewDidDisappear回调方法。

这里需要注意一点,在viewDidLoad/loadView以及viewWillAppear/viewDidAppear中,获取的UIViewController的view的frame都是不一定是准确的最终的frame,只有在UIViewController的viewDidLayoutSubviews回调中,才可以准确获取UIViewController的view的frame。

二. Hierarchy & Container View Controller


自定义viewcontroller总结_第1张图片
如上图,View有一个View Hierarchy,同时,View Controller也有一个View Controller Hierarchy。一般情况下,View Hierarchy 和 View Controller Hierarchy需要保持一致性。

负责一个或多个 View Controller 的展示并对其视图生命周期进行管理的对象,称之为容器,大部分容器本身也是一个View Controller,这样的容器称为 Container View Controller。常见的容器有 UINavigationController,UITabbarController等。

例如,UINavigationController的view and view controller hierarchy关系图:

自定义viewcontroller总结_第2张图片

对于container view controller,有以下特征:
  1. 提供对 Child View Controller 进行管理的接口,比如UINavigationController的push/pop等一系列接口
  2. 负责child view controller 的 appearance callback调用以及旋转事件的传递,viewWillAppear/viewDidAppear/viewWillDisappear/viewDidDisappear
  3. 保证View Hierarchy 与 View Controller Hierarchy一致

三. Custom Container View Controller

与此相关的方法其实不多,可以参见UIViewController.h头文件中,相关的方法都定义在UIContainerViewControllerProtectedMethods与UIContainerViewControllerCallbacks这两个category中,第一个category中的方法主要是用来调用管理关系层次;第二个则是UIViewController的回调方法,可以覆盖用来某个时间点上被回调。

@property(nonatomic,readonly) NSArray *childViewControllers NS_AVAILABLE_IOS(5_0);  // 获取所有的childViewController数组
- (void)addChildViewController:(UIViewController *)childController NS_AVAILABLE_IOS(5_0); // 添加一个child view controller
- (void) removeFromParentViewController NS_AVAILABLE_IOS(5_0); // 将controller从parent view controller中移除
上面这三个方法比较容易理解,不多说

/*
  This method can be used to transition between sibling child view controllers. The receiver of this method is
  their common parent view controller. (Use [UIViewController addChildViewController:] to create the
  parent/child relationship.) This method will add the toViewController's view to the superview of the
  fromViewController's view and the fromViewController's view will be removed from its superview after the
  transition completes. It is important to allow this method to add and remove the views. The arguments to
  this method are the same as those defined by UIView's block animation API. This method will fail with an
  NSInvalidArgumentException if the parent view controllers are not the same as the receiver, or if the
  receiver explicitly forwards its appearance and rotation callbacks to its children. Finally, the receiver
  should not be a subclass of an iOS container view controller. Note also that it is possible to use the
  UIView APIs directly. If they are used it is important to ensure that the toViewController's view is added
  to the visible view hierarchy while the fromViewController's view is removed.
*/
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(5_0);

这个方法比较有迷惑性,需要注意查看上面的注释说明,这个方法其实是用来管理view hierarchy,并不会影响 view controller hierarchy,所以这里你仍然需要自己管理view controler hierarchy。这个方法同时也比较有用,比如在切换child view controller的显示时,这个方法更加好用,可以参考官方guide中的代码实现:
- (void) cycleFromViewController: (UIViewController*) oldC
            toViewController: (UIViewController*) newC
{
    [oldC willMoveToParentViewController:nil];                        // 1
    [self addChildViewController:newC];

    newC.view.frame = [self newViewStartFrame];                       // 2
    CGRect endFrame = [self oldViewEndFrame];

    [self transitionFromViewController: oldC toViewController: newC   // 3
          duration: 0.25 options:0
          animations:^{
             newC.view.frame = oldC.view.frame;                       // 4
             oldC.view.frame = endFrame;
           }
           completion:^(BOOL finished) {
             [oldC removeFromParentViewController];                   // 5
             [newC didMoveToParentViewController:self];
            }];
}

我们在自定义这个时候,可以在parent view controller的init方法中,alloc一个child view controller,并把add这个child view controller到parent view controller中,但是需要在viewDidLoad/loadView中,才把child view controller的view添加到parent view controller的view中,这样做,可以让代码在相应的生命周期做相应的事情,在init中处理并初始化基本数据,在viewDidLoad/loadView中处理view hierarchy。

四. 总结

1. 注意保持view controller hierarchy 与 view hierarchy保持一致,否则会出现奇怪的crash问题

五. 总结

开发Android的时候,非常羡慕Android中的Fragment,用来实现复杂的逻辑非常合适,而且生命周期也非常完善,可以把复杂逻辑拆分开来,让代码层次非常清晰。iOS中,很早之前经常使用自定义View来实现这些,把View当做一种类似 View Controller的实现了,但是这里有时也会有比较多的问题,最明显的就是生命周期的管理,需要在View Controller中对child view的各种方法进行回调,这个无疑非常繁琐。去年的时候,我尝试使用一些child view controller来做事情,发现的确可以让代码层次清晰很多,虽然跟android的fragment无法相比,不过已经是非常有用了。

六. 参考

  • https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
  • https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/BasicViewControllers/BasicViewControllers.html
  • http://geeklu.com/2012/05/custom-container-view-controller/
  • http://stackoverflow.com/questions/8453653/proper-usage-of-transitionfromviewcontrollertoviewcontrollerdurationoptionsa

你可能感兴趣的:(自定义viewcontroller总结)