iOS实现容器视图控制器的方法

一直以来想写一个抽屉效果,看了一些文章后发现并不是那么简单,网上的一些抽屉效果不是很严谨。看了下MMDrawerController的源码,等于定制了一个Container View Controller。(类似于系统的UINavigationController以及UITabbarController);

比如下面几个方法就是MMDrawerController实现的:

下面的描述是官方文档帮助理解什么是容器控制器,文档中以UINavigationController和UISplitViewController举例。

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

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

几乎在任何情况下,容器视图控制器都像其他任何内容视图控制器一样管理根视图和一些内容。区别在于容器视图控制器从其他视图控制器获取其内容的一部分。它获取的内容仅限于其他视图控制器的视图,它嵌入在其自己的视图层次结构中。容器视图控制器设置任何嵌入视图的大小和位置,但原始视图控制器仍然管理这些视图内的内容。

在设计自己的容器视图控制器时,请始终了解容器和包含的视图控制器之间的关系。视图控制器的关系可以帮助告知他们的内容应该如何显示在屏幕上,以及你的容器如何在内部管理它们。在设计过程中,问自己以下问题:

1 .容器的作用是什么?它的孩子扮演什么样的角色?

2.多少个孩子同时显示?

3.兄弟视图控制器之间有什么关系(如果有的话)?

4.子视图控制器如何添加到容器或从容器中移除?

5.孩子的大小或位置能改变吗?这些变化在什么情况下发生?

6.容器是否提供任何装饰或导航相关的视图?

7.集装箱和子公司之间需要什么样的沟通?容器是否需要向小孩报告特定事件,而不是由UIViewController班级定义的标准事件?

8.容器的外观是否可以用不同的方式配置?如果是这样,怎么样?

在定义了各种对象的角色之后,容器视图控制器的实现相对简单。UIKit唯一的要求就是在容器视图控制器和任何子视图控制器之间建立正式的父子关系。亲子关系确保孩子们收到任何相关的系统消息。除此之外,大部分实际工作都发生在包含视图的布局和管理过程中,对于每个容器来说都是不同的。您可以将视图放置在容器的内容区域的任何位置,然后根据需要调整视图的大小。您还可以将自定义视图添加到视图层次结构中,以提供修饰或辅助导航。

示例:导航控制器

UINavigationController对象通过分层数据集支持导航。导航界面一次显示一个子视图控制器。界面顶部的导航栏显示数据层次结构中的当前位置,并显示后退按钮以向后移动一个级别。向下导航到数据层次结构留给子视图控制器,可以涉及使用表或按钮。

视图控制器之间的导航由导航控制器及其子节点联合管理。当用户与子视图控制器的按钮或表格行交互时,孩子要求导航控制器将新的视图控制器推入视图。孩子处理新的视图控制器的内容的配置,但导航控制器管理过渡动画。导航控制器还管理导航栏,该导航栏显示关闭最上面的视图控制器的后退按钮。

图5-1显示了导航控制器及其视图的结构。大多数内容区域由最顶层的子视图控制器填充,只有一小部分被导航栏占用。

在紧凑和常规的环境中,导航控制器一次只显示一个子视图控制器。导航控制器调整其子以适应可用空间。

示例:分割视图控制器

一个UISplitViewController对象以主 - 细节布局显示两个视图控制器的内容。在这种安排中,一个视图控制器(主视图)的内容决定了其他视图控制器显示的细节。两个视图控制器的可见性是可配置的,但也受当前环境的支配。在规则的水平环境中,分割视图控制器可以同时显示两个子视图控制器,也可以隐藏主控并根据需要显示。在紧凑的环境中,分割视图控制器一次只显示一个视图控制器。

图5-2显示了在一个常规的水平环境中的分割视图界面及其视图的结构。分割视图控制器本身只有默认的容器视图。在这个例子中,两个子视图是并排显示的。子视图的大小是可配置的,主视图的可见性也是可配置的。

在界面构建器中配置容器

要在设计时创建父子容器关系,请将容器视图对象添加到故事板场景中,如图5-3所示。容器视图对象是代表子视图控制器内容的占位符对象。使用该视图来调整和定位与容器中其他视图相关的子视图。

当您使用一个或多个容器视图加载视图控制器时,Interface Builder还会加载与这些视图关联的子视图控制器。孩子必须与父母同时实例化,以便建立适当的亲子关系。

如果您不使用Interface Builder设置父 - 子容器关系,则必须通过将每个子项添加到容器视图控制器来以编程方式创建这些关系,如将子视图控制器添加到您的内容中所述。

实现自定义容器视图控制器

要实现一个容器视图控制器,你必须建立你的视图控制器和它的子视图控制器之间的关系。在尝试管理任何子视图控制器的视图之前,建立这些父子关系是必需的。这样做让UIKit知道你的视图控制器正在管理孩子的大小和位置。您可以在Interface Builder中创建这些关系,或以编程方式创建它们。以编程方式创建父子关系时,您明确地添加和删除子视图控制器作为视图控制器设置的一部分。

将子视图控制器添加到您的内容

要以编程方式将子视图控制器合并到内容中,请执行以下操作,在相关的视图控制器之间创建父子关系:

调用addChildViewController:你的容器视图控制器的方法。

这个方法告诉UIKit你的容器视图控制器现在正在管理子视图控制器的视图。

将孩子的根视图添加到容器的视图层次结构中。

一定要记住设置孩子框架的大小和位置,作为这个过程的一部分。

添加任何约束来管理子视图的大小和位置。

调用didMoveToParentViewController:子视图控制器的方法。

清单5-1展示了一个容器如何在其容器中嵌入一个子视图控制器。建立父子关系后,容器设置其子的框架,并将子视图添加到自己的视图层次结构中。设置子视图的框架大小很重要,并确保视图在容器中正确显示。在添加视图后,容器调用didMoveToParentViewController:子视图的方法,使子视图控制器有机会响应视图所有权的更改。

清单5-1将一个子视图控制器添加到一个容器

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

在前面的例子中,注意你只调用didMoveToParentViewController:子方法。那是因为该方法为你addChildViewController:调用孩子的willMoveToParentViewController:方法。您必须didMoveToParentViewController:自己调用方法的原因是,只有在将子视图嵌入到容器的视图层次结构中之后,方法才能被调用。

使用自动布局时,在将子对象添加到容器的视图层次结构后,在容器和子对象之间设置约束。你的约束应该只影响孩子的根视图的大小和位置。请勿更改子视图层次结构中的根视图或任何其他视图的内容。

删除子视图控制器

要从内容中删除子视图控制器,请通过执行以下操作来删除视图控制器之间的父子关系:

willMoveToParentViewController:用值 调用孩子的方法nil。

删除您使用该子视图的配置的任何约束。

从容器的视图层次结构中移除孩子的根视图。

调用孩子的removeFromParentViewController方法来完成亲子关系的结束。

删除子视图控制器会永久切断父级和子级之间的关系。只有当您不再需要引用子视图控制器时,才能移除子视图控制器。例如,当新导航控制器被推入导航堆栈时,导航控制器不会移除其当前的子视图控制器。只有当它们从堆栈中弹出时才会被删除。

清单5-2显示了如何从容器中删除子视图控制器。willMoveToParentViewController:使用该值调用该方法nil使子视图控制器有机会为更改做准备。该removeFromParentViewController方法还调用孩子的didMoveToParentViewController:方法,传递该方法的值nil。设置父视图控制器以nil完成从容器中删除子视图。

清单5-2从容器中删除一个子视图控制器

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

子视图控制器之间的过渡

当您想要用另一个子视图控制器替换动画时,将子视图控制器的添加和删除合并到转换动画过程中。在动画之前,确保两个子视图控制器都是你的内容的一部分,但让当前的孩子知道它即将消失。在您的动画中,将新的孩子的视图移动到位并移除旧的孩子的视图。完成动画后,完成子视图控制器的移除。

清单5-3显示了如何使用过渡动画将一个子视图控制器交换为另一个子视图控制器的示例。在这个例子中,新的视图控制器被动画为现有的子视图控制器当前占用的矩形,该控制器被移出屏幕。动画完成后,完成块从容器中删除子视图控制器。在这个例子中,该transitionFromViewController:toViewController:duration:options:animations:completion:方法自动更新容器的视图层次,所以你不需要自己添加和删除视图。

清单5-3两个子视图控制器之间的转换

- (void)cycleFromViewController: (UIViewController*) oldVC
    toViewController: (UIViewController*) newVC {
 // 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];
  }];
}

管理儿童的外观更新

在将容器添加到容器后,容器会自动将外观相关的消息转发给子容器。这通常是您想要的行为,因为它确保所有事件都能正确发送。但是,有时默认行为可能会以对您的容器无意义的顺序发送这些事件。例如,如果多个孩子同时改变其视图状态,则可能需要合并这些更改,以使外观回调都以更合理的顺序同时发生。

为了接管外观回调的责任,重写shouldAutomaticallyForwardAppearanceMethods容器视图控制器中的方法并返回NO,如清单5-4所示。返回NO让UIKit知道你的容器视图控制器通知其子的外观变化。

清单5-4禁用自动外观转发

- (BOOL) shouldAutomaticallyForwardAppearanceMethods {
 return NO;
}

当出现外观转换时,根据需要调用子对象beginAppearanceTransition:animated:或endAppearanceTransition方法。例如,如果您的容器有一个由child属性引用的单个子项,那么您的容器会将这些消息转发给子项,如清单5-5所示。

清单5-5当容器出现或消失时转发外观消息

-(void) viewWillAppear:(BOOL)animated {
 [self.child beginAppearanceTransition: YES animated: animated];
}

-(void) viewDidAppear:(BOOL)animated {
 [self.child endAppearanceTransition];
}

-(void) viewWillDisappear:(BOOL)animated {
 [self.child beginAppearanceTransition: NO animated: animated];
}

-(void) viewDidDisappear:(BOOL)animated {
 [self.child endAppearanceTransition];
}

建立一个容器视图控制器的建议

设计,开发和测试新的容器视图控制器需要时间。虽然个人行为是直截了当的,但整个控制者可能相当复杂。在实现自己的容器类时,请考虑以下提示:

只访问子视图控制器的根视图。一个容器只能访问每个孩子的根视图,也就是孩子view属性返回的视图。它不应该访问任何孩子的其他意见。

子视图控制器应该对其容器有最少的了解。子视图控制器应该关注自己的内容。如果容器允许其行为受到孩子的影响,则应该使用委托设计模式来管理这些交互。

首先使用常规视图设计您的容器。使用常规视图(而不是来自子视图控制器的视图)使您有机会在简化的环境中测试布局约束和动画过渡。当常规视图按预期工作时,将它们交换为您的子视图控制器的视图。

将控制委派给子视图控制器

容器视图控制器可以将其自身外观的某些方面委托给其一个或多个子级。您可以通过以下方式委托控制:

  1. 让一个子视图控制器确定状态栏的样式。要委派状态栏外观小孩,覆盖的一个或两个childViewControllerForStatusBarStyle,并childViewControllerForStatusBarHidden在你的容器视图控制器的方法。
  2. 让孩子指定自己喜欢的尺寸。具有灵活布局的容器可以使用孩子自己的preferredContentSize财产来帮助确定孩子的大小。
(0)

相关推荐

  • iOS实现容器视图控制器的方法

    一直以来想写一个抽屉效果,看了一些文章后发现并不是那么简单,网上的一些抽屉效果不是很严谨.看了下MMDrawerController的源码,等于定制了一个Container View Controller.(类似于系统的UINavigationController以及UITabbarController); 比如下面几个方法就是MMDrawerController实现的: 下面的描述是官方文档帮助理解什么是容器控制器,文档中以UINavigationController和UISplitViewC

  • 实例讲解iOS中的UIPageViewController翻页视图控制器

    一.引言 UIPageViewController是iOS中少见的动画视图控制器之一,通过它既可以创建类似UIScrollView与UIPageControl结合的滚屏视图,也可以创建类似图书效果的炫酷翻页视图. UIPageViewController在iOS 5 SDK中首次引入,它使得开发者可以使用这个ViewController创建分页视图.在iOS 6中,这个类有了更新,支持滚动过渡效果.使用Page View,用户可以方便的通过手势在多个页面之间导航.UIPageViewContro

  • IOS视图控制器的生命周期实例详解

    IOS视图控制器 所谓的生命周期,也就是几个函数的调用顺序,这里以用Storyboard来创建一个ViewController为例 然后我们测试如下代码 // // ViewController.m // // Created by huangwenchen on 14/12/26. // Copyright (c) 2014年 huangwenchen. All rights reserved. // #import "ViewController.h" @interface Vie

  • IOS开发OC代码中创建Swift编写的视图控制器

    IOS开发OC代码中创建Swift编写的视图控制器 背景 近日在和一群朋友做项目,我和另一位同学负责iOS客户端,我是一直使用OC的,而他只会Swift,因此在我们分工协作之后,就需要把代码合在一起,这就牵扯到如何在TabbarController中添加一个swift创建的子控制器的问题. 解决 首先在一个OC项目中新建一个Swift类,继承自UITableViewController,并且修改其view背景色,方便后续测试. import UIKit class ESSwiftTableVie

  • Yii控制器中操作视图js的方法

    本文实例讲述了Yii控制器中操作视图js的方法.分享给大家供大家参考,具体如下: //YII framework路径 Yii::getFrameworkPath(); //protected/runtime Yii::app()->getRuntimePath(); //protected/venders目录 Yii::import('application.venders.*'); //或在protected/config/main.php说明 'import'=>array( //....

  • iOS开发之视图切换

    一.视图切换 UITabBarController (分页控制器) - 平行管理视图 UINavigationController (导航控制器) - 压栈出栈管理视图 模态窗口 二.UITabBarController分页控制器 UITabBarController是为了利用 页签切换视图 设计的控制器 该控制器有一个UITabBar控件,用户通过点击UITabBar进行视图切换 UITabBarController本身会不显示任何视图,它只是一个 容器控制器 为了减少视图间的耦合,所有UIT

  • IOS App 无代码入侵的方法hook详细介绍

    iOS App 无代码入侵的方法hook 继续Objective-C runtime的研究 最近公司项目在做用户行为分析 于是App端在某些页面切换,交互操作的时候需要给统计系统发送一条消息 在几十个Controller 的项目里,一个一个地加代码那完全是不可能的,维护起来也是吃力 但这里需要处理的是 Controller, 可以有以下方式实现上述需求 1. 利用Objective-C 中的对象继承 继承 在面向对象开发中是非常常用的,像我们现在做的项目工程中都会有一个BaseViewContr

  • iOS push侧滑返回功能实现方法

    本文实例为大家分享了iOS push侧滑返回功能的具体代码,供大家参考,具体内容如下 开启iOS自带的push的侧滑返回功能(只有左侧边缘地带响应侧滑返回,并不是全局响应): - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) { [self.navigationContr

  • 详解iOS开发获取当前控制器的正取方式

    背景 在开发过程中,经常需要获取当前 window, rootViewController, 以及当前 ViewController 的需求. 如果 .m 实现不是在当前视图情况下, 我们需要快速的获取到当前控制器, 这种情况就需要先做好一层封装,我一般是通过 UIViewController 写的一个 Category 来实现, 实现起来也非常简单, 只需要我们对 控制器几个方法掌握便可. 获取根控制器 + (UIViewController *)jsd_getRootViewControll

  • iOS中状态栏的基本使用方法汇总

    一.状态栏与导航栏 状态栏:显示时间.电池等信息 导航栏:显示app页面标题,返回按钮等 iOS7之前:状态栏与导航栏是分开的: iOS7之后:状态栏与导航栏合在一起:导航部分总高度(64)= 状态栏高度(20) +导航栏内容高度((44) iPhoneX设备出现以后,状态栏的高度变为44,导航栏部分总高度(88) = 状态栏(44) + 导航栏内容高度(44) 二.设置状态栏显隐与字体样式 iOS状态栏可以设置显示和隐藏,也可以设置文字的颜色.通过修改info.plist中的View cont

随机推荐