自定义容器视图控制器总结

自定义容器视图控制器总结

同时发布于知乎

1.Apple文档对容器视图器的解释

容器视图控制器是将来自多个视图控制器的内容合并到单个用户界面中的一种方法。容器视图控制器通常用于导航,并基于现有内容创建新的用户界面类型。UIKit中的容器视图控制器的例子包括UINavigationController,UITabBarController和UISplitViewController,所有这些都方便您的用户界面的不同部分之间进行交互.

2.设计自定义容器视图控制器

在设计自己的容器视图控制器时,明确容器角色以及容器和包含的视图控制器之间的关系。在设计过程中,思考如下问题:

1.容器是什么角色?承担哪些职责?
2.有哪些Chil-VC? 他们之间是什么关系?
3.子视图控制器如何添加到容器或从容器中移除?4.需要手动控制child-vc生命周期吗? Apprease
5.过度动画如何设计?
6.需要支持转屏吗?
7.容器与Child VC 以及 Child VC 间 ,他们该如何通信

3.容器相关知识点

3.1掌握 UIViewController 生命周期

3.2 添加子视图控制器

- (void) displayContentController: (UIViewController*) content {
    [self addChildViewController:content];
    content.view.frame = [self frameForContentController];
    [self.view addSubview:self.currentClientView];
    [content didMoveToParentViewController:self];
}

解释:

1)调用容器视图控制器的addChildViewController:,此方法是将子视图控制器添加到容器视图控制器,告诉UIKit父视图控制器现在要管理子视图控制器和它的视图。
 2)调用 addSubview: 方法,将子视图控制器的根视图加在父视图控制器的视图层级上。这里需要设置子视图控制器中根视图的位置和大小。
 3)布局子视图控制器的根视图。
 4)调用 didMoveToParentViewController:,告诉子视图控制器其根视图的被持有情况。也就是需要先把子视图控制器的根视图添加在父视图中的视图层级中。

在调用 addChildViewController: 时,系统会先调用 willMoveToParentViewController: 
然后再将子视图控制器添加到父视图控制器中。
但是系统不会自动调用 didMoveToParentViewController: 方法需要手动调用
为何呢?
视图控制器是有转场动画的,动画完成后才应该去调用 didMoveToParentViewController: 方法。

3.3 移除子视图控制器

- (void) hideContentController: (UIViewController*) content {
    [content willMoveToParentViewController:nil];
    [content.view removeFromSuperview];
    [content removeFromParentViewController];
}

解释:

1)调用子视图控制器的willMoveToParentViewController:,参数为 nil,让子视图做好被移除的准备。
 2)移除子视图控制器的根视图在添加时所作的任何的约束布局。
 3)调用 removeFromSuperview 将子视图控制器的根视图从视图层次结构中移除。
 4)调用 removeFromParentViewController 来告知结束父子关系。
 5)在调用 removeFromParentViewController 时会调用子视图控制器的 didMoveToParentViewController: 方法,参数为 nil。

3.4 子视图控制器之间的过渡动画

-(void)transitionFromViewController:(UIViewController *)fromViewController
                   toViewController:(UIViewController *)toViewController 
                           duration:(NSTimeInterval)duration
                            options:(UIViewAnimationOptions)options
                         animations:(void (^)(void))animations
                         completion:(void (^)(BOOL finished))completion;

*Note
函数内部系统会会自动添加新的视图和移除之前的视图

具体事例:

基本原理:
当需要子视图控制器过渡到另一个视图控制器的动画,结合子视图控制器的添加和删除到过渡动画过程。在动画之前,确保两个子视图控制器是内容的一部分,让当前的子视图消失。在动画过程中,移动新子视图控制器到相应的位置并删除旧的子视图控制器。在动画完成之后,删除子视图控制器。

// Prepare the two view controllers for the change.
    [oldVC willMoveToParentViewController:nil];
    [self addChildViewController:newVC];

    // Get the start frame of the new view controller and the end frame
    // for the old view controller. Both rectangles are offscreen.
    newVC.view.frame = [self newViewStartFrame];
    CGRect endFrame = [self oldViewEndFrame];

    // Queue up the transition animation.
    [self transitionFromViewController: oldVC toViewController: newVC
        duration: 0.25 options:0
        animations:^{
        // Animate the views to their final positions.
        newVC.view.frame = oldVC.view.frame;
        oldVC.view.frame = endFrame;
        }
        completion:^(BOOL finished) {
        // Remove the old view controller and send the final
        // notification to the new view controller.
        [oldVC removeFromParentViewController];
        [newVC didMoveToParentViewController:self];
        }];

上面的事例有一个缺陷:无法结合Autolayout来布局,原因

// XXX We can't add constraints here because the view is not yet in the view hierarchy
    // animation setup 

代码二次改造:

- (void) performTransitionFromViewController:(UIViewController*)fromVc toViewController:(UIViewController*)toVc {

    [fromVc willMoveToParentViewController:nil];
    [self addChildViewController:toVc];

    UIView *toView = toVc.view;
    UIView *fromView = fromVc.view;

    [self.containerView addSubview:toView];

    // TODO: set initial layout constraints here

    [self.containerView layoutIfNeeded];

    [UIView animateWithDuration:.25
                          delay:0
                        options:0
                     animations:^{

                         // TODO: set final layout constraints here

                         [self.containerView layoutIfNeeded];

                     } completion:^(BOOL finished) {
                         [toVc didMoveToParentViewController:self];
                         [fromView removeFromSuperview];
                         [fromVc removeFromParentViewController];
                     }];
}

3.5 appearance callbacks的传递

////设置为NO,屏蔽对childViewController的生命周期函数的自动调用,改为手动控制
- (BOOL)shouldAutomaticallyForwardAppearanceMethods {
    return NO;
}

容器控制器就要在子控制出现和消失时通知子控制器,
分别通过调用子控制器的 
beginAppearanceTransition:animated: 
&&
endAppearanceTransition():
实现,不需要直接调用子控制器的
 viewWillAppear:
 viewDidAppear:
 viewWillDisappear:
 viewDidDisappear: 方法

 另外注意的是:
 begin和end必须成对出现
 [content beginAppearanceTransition:YES animated:animated]触发content的viewWillAppear,
 [content beginAppearanceTransition:NO animated:animated]触发content的viewWillDisappear,
 和他们配套的[content endAppearanceTransition]
 分别触发viewDidAppear和viewDidDisappear。

3.6 rotation callbacks的传递

rotation callbacks 一般情况下只需要关心三个方法 :
willRotateToInterfaceOrientation:duration:
在旋转开始前,此方法会被调用;
willAnimateRotationToInterfaceOrientation:duration: 
此方法的调用在旋转动画block的内部,也就是说在此方法中的代码会作为旋转animation block的一部分;
didRotateFromInterfaceOrientation:
此方法会在旋转结束时被调用。
而作为view controller container 就要肩负起旋转的决策以及旋转的callbacks的传递的责任。

禁用方式:

禁掉默认调用需要重写两个方法 
shouldAutorotate:
supportedInterfaceOrientations:

前者决定再旋转的时候是否去根据supportedInterfaceOrientations所支持的取向来决定是否旋转;
假如shouldAutorotate返回YES的时候,才会去调用supportedInterfaceOrientations检查当前view controller支持的取向,
如果当前取向在支持的范围中,则进行旋转,
如果不在则不旋转;
而当shouldAutorotate返回NO的时候,则根本不会去管supportedInterfaceOrientations这个方法,反正是不会跟着设备旋转就是了。

在编写的容器组过程中,先去检查你的child view controller对横竖屏的支持情况,以便容器自己决策在横竖屏旋转时候是否支持当前的取向

- (BOOL)shouldAutorotate {
    UIViewController *visibleViewController ;
    if (visibleViewController != self && [visibleViewController respondsToSelector:@selector(shouldAutorotate)]) {
        return [visibleViewController shouldAutorotate];
    }
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    UIViewController *visibleViewController = ;
    if (visibleViewController != self && [visibleViewController respondsToSelector:@selector(supportedInterfaceOrientations)]) {
        return [visibleViewController supportedInterfaceOrientations];
    }
    return self.supportedOrientationMask;
}

3.6 其他

1.重载 childViewControllerForStatusBarStyle 属性,返回相应的子控制器,让子控制器决定状态栏样式。当这个属性发生变化,调用 setNeedsStatusBarAppearanceUpdate() 方法更新状态栏样式。

2.容器控制器可以用子控制器的 preferredContentSize 属性决定子控制器 view 的大小。

参考

1.containing-viewcontrollers
2.Apple文档
3.Autolayout

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342