使用Unwind Segues
翻译自“Using Unwind Segues”
本文档是关于unwind segue的概述。
1 介绍
通过push,modal,popover和其他类型的segues添加到导航栈(navigation stack)的视图控制器,可以通过unwind segue从导航栈返回。使用unwind segue可以从导航层次(navigation hierarchy)中返回上一步或上几步。普通的segue会创建一个目标视图控制器的新实例,然后转换;而unwind segue转换到导航层次中已经存在的视图控制器。转换开始前,为源和目标视图控制器提供回调函数。通过这些回调函数在两个视图控制器之间传递数据。
在Interface Builder中创建一个unwind segue时,不需要指定目标视图控制器。当unwind segue触发时,运行时动态判断跳转到导航层次中哪一个视图控制器。这个过程可自定义,并为容器视图控制器提供额外可自定义的方面。
2 创建Unwind Segue
在Interface Builder中添加unwind segue之前,至少需要定义一个unwind action。Unwind action是一个实例方法,UIStoryboardSegue是唯一的参数,返回类型为IBAction。对方法名没有特殊的要求,但是方法名在整个应用程序必须是唯一的和有意义的。跟IBAction和IBOutlet一样,unwind action不需要在头文件中声明。有一个例外,如果开发一个框架,希望框架的使用者创建一个能触发框架中action的unwind segue时,需要在头文件中声明。
清单 1:定义unwind action的例子
/* Objective-C */
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
UIViewController *sourceViewController = sender.sourceViewController;
// Pull any data from the view controller which initiated the unwind segue.
}
/* Swift */
@IBAction func unwindToMainMenu(sender: UIStoryboardSegue)
{
let sourceViewController = sender.sourceViewController
// Pull any data from the view controller which initiated the unwind segue.
}
在storyboard中创建unwind segue时,在unwind segue要返回的视图控制器中指定unwind action的名称。该unwind action在unwind segue触发前执行。通过UIStoryboardSegue参数的sourceViewController,接收来自初始化unwind segue的视图控制器的数据。
2.1 添加Unwind Segue到Storyboard
使用control+drag从场景(scene)中的一个对象(按钮、列表视图单元格),到场景的退出图标,来添加一个unwind segue,如图1中的高亮显示。从弹出的菜单中为该segue选择一个unwind action。
图 1:场景的退出图标是右上角的橙色图标
要添加一个只能代码触发的unwind segue,control+drag从场景的视图控制器图标到退出图标,如图2。从弹出的菜单中选择一个unwind action。为了能从代码引用segue,需要添加segue的标识符。一个unwind segue通过发送performSegueWithIdentifier:sender:消息触发。
图 2:创建一个在代码中使用的unwind segue
添加一个unwind segue后,会出现在场景的提纲中,如图3所示。
图 3:每个场景中的unwind segue
3 Unwind过程
本节描述发生在unwind segue初始化后的过程,包括在场景中使用控件交互,和在视图控制器中发送performSegueWithIdentifier:sender:消息。
1. Unwind Segue遍历导航层次定位到目标视图控制器。
2. 初始化unwind segue的视图控制器发送prepareForSegue:sender:消息。默认的实现什么也不做。视图控制器通过覆写这个方法,传递数据到目标视图控制器。
3. 目标视图控制器的unwind action被触发。
4. Unwind segue在源和目标视图控制器之间执行转场动画。
容器视图控制器有额外的职责。
3.1 Unwind Segues怎么确定目标视图控制器
一个unwind segue初始化后,首先需要找到导航层次中最近的,实现了unwind segue创建时指定的unwind action的视图控制器。这个视图控制器就是unwind segue的目标视图控制器。如果没有找到合适的视图控制器,unwind segue会被终止。
从初始化unwind segue的视图控制器开始,查找顺序为:
1. 响应链的下一个是发送了viewControllerForUnwindSegueAction:fromViewController:withSender:消息的视图控制器。模态显示的视图控制器,是调用了presentViewController:animated:completion:的视图控制器,否则就是parentViewController。
默认的实现是,搜索接收者的childViewControllers数组中想要处理unwind action的视图控制器。如果接收者的子视图控制器都不想处理unwind action,接收者检测自己是否想要处理unwind action,如果想就返回self。两种情况下,使用canPerformUnwindSegueAction:fromViewController:withSender:判断视图控制器是否想要处理unwind action。
2. 如果第一步中,没有视图控制器从viewControllerForUnwindSegueAction:fromViewController:withSender:方法返回,从响应链下一个view controller重复搜索。
如果接收者响应该action,canPerformUnwindSegueAction:fromViewController:withSender:的默认实现是返回YES。如果需要额外的条件来确定能否成为unwind segue的目标视图控制器,可以覆写 canPerformUnwindSegueAction:fromViewController:withSender:。为了消除导航栈中有多个同一个视图控制器实例的歧义,这有可能是必须的。
重要:如果视图控制器覆写 canPerformUnwindSegueAction:fromViewController:withSender:,只当接收者想要处理unwind action时返回YES。不要代表子视图控制器返回YES。
4 容器视图控制器的职责
在unwind过程中,容器视图控制器有两个职责。如果使用SDK提供的容器视图控制器(UINavigationController,UITabBarController,...),这些都自动处理了。
4.1 选择一个子视图控制器来处理Unwind Action
如果自定义的逻辑需要判断容器的哪一个子视图控制器处理unwind action,就应该实现清单2中的方法。例如,UINavigationController的实现是从导航层次的最底层开始搜索。如果容器的所有子视图控制器都不处理,就触发父类的实现并返回结果。
清单 2:覆写这个方法返回想要处理unwind action的子视图控制器
/* Objective-C */
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
/* Swift */
override func viewControllerForUnwindSegueAction(action: Selector, fromViewController: UIViewController, withSender sender: AnyObject?) -> UIViewController?
提示:总是使用canPerformUnwindSegueAction:fromViewController:withSender:确定视图控制器是否想要处理unwind action。
4.2 两个子视图控制器之间的转场
当unwind segue找到目标视图控制器,它尝试为转场找到unwind executor。Unwind executor是初始化unwind segue的视图控制器和目标视图控制器的普通原型。它的职责是创建一个UIStoryboardSegue,用来执行源和目标视图控制器的可视化转场。
当容器视图控制器从viewControllerForUnwindSegueAction:fromViewController:withSender:返回一个视图控制器,容器视图控制器成为转场的unwind executor。因此,容器视图控制器需要实现清单3中的方法,该方法返回UIStoryboardSegue,它执行从源视图控制器到目标视图控制器转场的动画和需要的其他步骤。返回的对象是一个从segueWithIdentifier:source:destination:performHandler:创建的UIStoryboardSegue实例,或者是自定义子类的实例。
清单3:实现该方法返回fromViewController和toViewController中间转场的UIStoryboardSegue
/* Objective-C */
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier
/* Swift */
override func segueForUnwindingToViewController(toViewController: UIViewController, fromViewController: UIViewController, identifier: String?) -> UIStoryboardSegue
提示:容器视图控制器和fromViewController之间可能有额外的视图控制器。不能假设fromViewController是容器的直接子视图控制器。