在屏幕上显示视图控制器有两种方法:嵌入到容器视图控制器中或者present这个视图控制器。容器视图控制器提供应用的主要导航,但present视图控制器同样也是一个重要的导航工具。可以在当前的视图控制器上直接present一个新的视图控制器。通常,当你希望实现modal界面时,可以present视图控制器,但你也可以使用它们用于其他用途。
UIViewController 类支持present视图控制器,可用于所有的视图控制器对象。你可以在任何其他视图控制器上present任何一个视图控制器,虽然UIKit可能重新路由该请求到不同的视图控制器上。present视图控制器会在原始视图控制器之间创建关系,原始视图控制器称为 presenting view controller,显示的新视图控制器称为presented view controller。这个关系是视图控制器层级结构的一部分,直到presented视图控制器被dismiss。
展示和过渡的过程
present一个视图控制器是一种快速而简单的方法在屏幕上推出新内容。UIKit内置present机制,允许使用内置或自定义动画来显示一个新的视图控制器。内置present和动画需要很少的代码因为UIKit处理了所有的工作。你也可以很容易的在任何视图控制器创建自定义present和动画。
你可以以编程方式或使用segue来发起视图控制器的present。如果在设计时你知道应用的导航,segue是发起present最简单的方式。更多动态界面,或者没有专门的控件发起segue的情况下,使用 UIViewController方法来present视图控制器。
Presentation Styles
presentation style 是一个视图控制器管理其屏幕上的外观。UIKit定义了许多标准的present风格,每个都有特定的外观和意图。你也可以定义自定义present风格。当设计你的应用时,为你的应用选择合适的present风格,并为要present的视图控制器的 modalPresentationStyle属性分配适合的常数。
Full-Screen Presentation Styles
Full screen presentation styles覆盖全部屏幕,防止与底层内容互动。在水平常规环境中,只有full-screen styles完全覆盖了底层内容。其余变暗或有透明度的视图允许部分底层视图控制器显示。在水平紧凑环境中,full-screen present方式自动采用UIModalPresentationFullScreen
风格并覆盖所有底层内容。
图8-1 说明在水平常规环境使用UIModalPresentationFullScreen,UIModalPresentationPageSheet和UIModalPresentationFormSheet风格present方式的外观。在图中,在左上角的绿色视图控制器present在右上角的蓝色视图控制器,各种present风格如下。对于一些present风格,UIKit在两个视图控制器内容之间插入一个半透明视图。
注意
当使用
UIModalPresentationFullScreen
风格present一个视图控制器时,UIKit通常在完成过渡动画后删除底层视图控制器的视图。你可以通过指定UIModalPresentationOverFullScreen风格防止删除这些视图。当present的视图控制器有透明区域让底层内容显示时可以采用这种风格。
当使用一种present 风格时,视图控制器启动的present必须覆盖整个屏幕。如果将要present的视图控制器不能覆盖整个屏幕,UIKit遍历视图控制器层级直到找到一个覆盖整个屏幕的视图控制器。如果不能找到一个中间视图控制器充满屏幕,UIKit使用窗口的根视图控制器。
The Popover Style
UIModalPresentationPopover风格以弹出窗口的方式显示在视图控制器上。popover用于显示额外信息或项目列表中选中的对象。在横向常规环境中,popover视图仅覆盖屏幕的一部分,如图8-2所示。在横向紧凑环境中,popover默认采用UIModalPresentationOverFullScreen present方式。在popover视图外点击, popover视图将自动dismiss。
因为在水平紧凑环境中,popover采用full-screen 的present方式,通常需要修改popover代码处理适配。在full-screen模式,需要一种方法dismiss掉present的popover。你可以通过添加一个按钮,将popover嵌入到将要dismiss的容器视图控制器,或者改变自适应行为本身。
如何配置弹窗,参见在视图控制器上present弹窗( Presenting a View Controller in a Popover)。
The Current Context Styles
UIModalPresentationCurrentContext 风格在界面覆盖了一个特殊的视图控制器。当使用contextual风格,通过设置 definesPresentationContext属性为YES,指定要覆盖的视图控制器。图8-3说明了current context 只覆盖分屏视图控制器的一个子视图控制器。
注意
当使用
UIModalPresentationFullScreen
风格present一个视图控制器,UIKit通常在过渡动画完成后删除底层视图控制器的视图。可以设置UIModalPresentationOverCurrentContext风格,防止删除那些视图。当present的视图控制器有透明区域让底层内容显示时,可以使用该风格。
定义视图控制器还可以在present过程中定义过渡动画。通常,当使用present视图控制器的 modalTransitionStyle属性,UIKit渲染视图控制器到屏幕。如果present context视图控制器的 providesPresentationContextTransitionStyle值设为YES,UIKit使用视图控制器的modalTransitionStyle
属性代替。
当过渡到水平紧凑的环境,current context风格采用 UIModalPresentationFullScreen 风格。为了改变该行为,使用自适应present委托指定不同的present风格或视图控制器。
Custom Presentation Styles
UIModalPresentationCustom风格使用自定义的风格present视图控制器。创建一个自定义风格包括子类化UIPresentationController ,使用其方法来渲染任何自定义视图到屏幕上并设置present视图控制器的大小和位置。present控制器还处理由于视图控制器的trait变化引起的适配。
如何定义自定义present视图控制器的信息,参见创建自定义present( Creating Custom Presentations)。
Transition Styles
Transition风格决定用于显示present视图控制器的动画类型。对于内置transition风格,你可以为present视图控制器的 modalTransitionStyle属性指定一种标准transition风格。当present视图控制器,UIKit创建相应的动画风格。例如,图8-4说明了标准slide-up过渡(UIModalTransitionStyleCoverVertical)如何视图控制器渲染到屏幕上。视图控制器B开始在屏幕外,并覆盖到视图控制器A上。当dismiss视图控制器B,动画逆转,B下滑露出A。
可以使用动画对象和过渡代理来创建自定义过渡。动画对象为屏幕上放置的视图控制器创建过渡动画。过渡代理在合适的时间为UIKit提供动画对象。更多关于如何实现自定义过渡的信息,参见自定义过渡动画(Customizing the Transition Animations)。
present和显示一个视图控制器
UIViewController类提供两种方法来显示一个视图控制器:
- showViewController:sender:和 showDetailViewController:sender:方法为显示视图控制器提供最大限度的适应性和灵活性。这些方法让present视图控制器决定如何最好的处理present。例如,容器视图控制器可能合并视图控制器作为子视图控制器而不是以模态的方式present。默认以模态的方式present视图控制器。
- presentViewController:animated:completion: 方法总是以模态的方式显示视图控制器。视图控制器调用这个方法可能不会最终处理present但总是以模态的方式present。在水平紧凑环境中,该方法采用该present风格。
showViewController:sender:
和showDetailViewController:sender:
方法是启动present的首选方法。视图控制器可以调用它们,而不用知道任何关于视图控制器层级结构或当前视图控制器在层级结构的位置。这些方法使重用视图控制器更加容易,而无需编写条件代码路径。
present视图控制器
有几种方法可以present视图控制器:
- 使用segue自动present视图控制器。使用界面构建器指定的信息,segue实例化并present视图控制器。关于如何配置segue的更多信息,参见使用segue( Using Segues)。
- 使用 showViewController:sender: 和 showDetailViewController:sender:方法显示视图控制器。在自定义视图控制器中,可以改变这些方法的行为使之更加适合你的视图控制器。
- 调用presentViewController:animated:completion:方法以模态的方式present视图控制器。
关于如何dismiss使用这些技术present的视图控制的更多信息,参见dismiss一个presented的视图控制器( Dismissing a Presented View Controller)。
显示视图控制器
当使用和方法,得到屏幕上一个新的视图控制器的过程很简单:
- 创建你希望present的视图控制器对象。当创建视图控制器时,你负责初始化它执行任何所需的任何数据。
- 设置新视图控制器的 modalPresentationStyle属性是首选present风格。该风格可能不用于最终的present。
- 设置视图控制器的modalTransitionStyle属性设置所需的过渡动画风格。该风格可能不用于最终的present。
- 调用当前视图控制器的 showViewController:sender:和showDetailViewController:sender:方法。
UIKit转发showViewController:sender:
和showDetailViewController:sender:
方法的调用给present视图控制器。视图控制器可以决定如何最好的执行present,并且可以根据需呀改变present和过渡风格。例如,导航控制器可能push该视图控制器到导航堆栈。
关于显示视图控制器和以模态的方式present视图控制器的差异信息,参见present和显示视图控制器( Presenting Versus Showing a View Controller)。
以模态的方式present视图控制器
当直接present视图控制器,你需要告诉UIKit你希望视图控制器如何显示以及如何渲染到屏幕上。
创建你希望present的视图控制器对象。当创建视图控制器时,你负责初始化它执行任何所需的任何数据。
- 设置新视图控制器的 modalPresentationStyle 属性为所需的present风格。
- 设置视图控制器的 modalTransitionStyle属性为所需的动画风格。
- 调用当前视图控制器的 presentViewController:animated:completion:方法。
调用presentViewController:animated:completion:
方法的视图控制器可能不是最终执行模态present的视图控制器。present风格决定了视图控制器如何被present,包括present视图控制器所需的特征。例如,一个全屏present必须由一个全屏视图控制器启动。如果当前present视图控制器不合适,UIKit遍历视图控制器层级直到找到合适的。完成模态present后,UIKit更新受影响的视图控制器的presentingViewController和 presentedViewController属性。
列表8-1 演示了如何以编程的方式present一个视图控制器。当用户添加一个新视图控制器,应用提示用户输入present导航视图控制器的基本信息。被选中的导航控制器会在标准的地方放置一个取消和完成按钮。使用导航控制器使扩展新界面更加容易。你所需要做的是push新视图控制器到导航堆栈。
列表8-1 以编程的方式present一个视图控制器
<pre><code>
- (void)add:(id)sender {
- // Create the root view controller for the navigation controller
- // The new view controller configures a Cancel and Done button for the
- // navigation bar.
- RecipeAddViewController *addController = [[RecipeAddViewController alloc] init];
- addController.modalPresentationStyle = UIModalPresentationFullScreen;
- addController.transitionStyle = UIModalTransitionStyleCoverVertical;
- [self presentViewController:addController animated:YES completion: nil];
- }
</pre></code>
以弹窗形式present视图控制器
在present视图控制器之前,popover需要额外的配置。在设置模态present风格为 UIModalPresentationPopover后,配置一下popover相关属性:
- 设置视图控制器的 preferredContentSize属性为所需的大小。
- 使用相关UIPopoverPresentationController对象设置popover锚点,该对象可用于视图控制器的 popoverPresentationController属性。设置一下其中一种:
- -设置属性为bar button。
- -设置 sourceView 和 sourceRect属性指定特定视图区域。
可以使用 UIPopoverPresentationController对象在需要时调整弹窗的外观。弹窗present控制器还支持代理对象,可以使用它来响应在present过程中的变化。例如,你可以使用代理来响应弹窗在屏幕上出现、消失或重新定位。关于该对象的更多信息,参见UIPopoverPresentationController类引用(UIPopoverPresentationController Class Reference)。
dismiss一个presented的视图控制器
调用presenting视图控制器的dismissViewControllerAnimated:completion:方法来dismiss一个presented的视图控制器。你可以在presented视图控制器本身的方法。当presented视图控制器调用该方法,UIKit自动转发请求到presenting视图控制器。
在dismiss视图控制器前保存任何重要信息。dismiss视图控制器会将其从视图控制器层级结构删除并从屏幕上删除视图。如果在其他地方存储的视图控制器没有强引用,dismiss该视图控制器会释放与之关联的内存。
如果presented视图控制器必须返回数据到presenting视图控制器,使用 delegation 设计模式来完成传递。代理使应用视图控制器的重用更容易。使用代理,presented视图控制器存储代理对象的引用,实现正式protocol的方法。Presented视图控制器在代理调用这些方法用于收集结果。在典型的实现中,presenting视图控制器作为presented视图控制器的代理。
present在不同storyboard种定义的视图控制器
虽然在同一storyboard中的两个视图控制器之间可以创建segue,但在不同storyboard中不能创建segue。当你希望显示存储在另一个storyboard中的视图控制器时,你必须在present之前显式的实例化该视图控制器,如列表8-2所示。例子以模态的方式present视图控制器,但你可以push它到导航控制器或以其他方式显示。
列表8-2 加载storyboard中的视图控制器
<pre><code>
- UIStoryboard* sb = [UIStoryboard storyboardWithName:@"SecondStoryboard" bundle:nil];
- MyViewController* myVC = [sb instantiateViewControllerWithIdentifier:@"MyViewController"];
- // Configure the view controller.
- // Display the view controller
- [self presentViewController:myVC animated:YES completion:nil];
</pre></code>
没有要求你必须在应用中创建多个storyboard。不过,在这里有一些情况下多个storyboard可能有用:
- 你有一个大的编程团队,不同的用户界面分配给团队的不同部分。每个团队拥有不同的storyboard文件以减少冲突。
- 你购买或创建一个库预定义了视图控制器类型集合;这些视图控制器的内容定义在库中的storyboard中。
- 你有需要在屏幕外显示的内容。在这种情况下,你可能保持所有视图控制器与单独storyboard的视图控制器相关。相同场景的另一种模式是编写一个自定义segue。