自iOS5引入Storyboard之后,iOS开发者在除了原有的Nib开发的基础上又有了新的方式来组织自己的UI和流程。Storyboard相对于传统的Nib,能够更加清晰的体现业务的流程,因此很受开发者欢迎。如今,很多教程都以Storyboard开发方式来讲解。而Storyboard中的Segue则是对转场流程的进一步封装。这个概念在Storyboard中至关重要,也是实现自定义转场的关键角色。
自定义Segue
自定义Segue的方式很简单,只要创建一个UIStoryboardSegue子类,并实现其perform
方法即可。一个简单的实现如下:
- (void)perform
{
// Modal presentation segue
UIViewController *fromController = self.sourceViewController;
UIViewController *toController = self.destinationViewController;
[fromController presentViewController:toController animated:YES completion:^{
// Completion code here
}];
}
或者
- (void)perform
{
// Navigation ViewController segue(Push segue, Show segue in iOS8).
UIViewController *fromController = self.sourceViewController;
UIViewController *toController = self.destinationViewController;
[fromController.navigationController pushViewController:toController animated:YES];
}
自定义Unwind Segue
自定义Unwind Segue的方式与上面几乎完全一样,只不过调用的接口由presentViewController:animated:completion:
和pushViewController:animated:
换成dismissViewControllerAnimated:completion:
和popToViewController:animated:
。
但是Unwind Segue与普通的Segue有一个很大的不同,就是Unwind Segue的调用通常是由一个Container View Controller完成的。在iOS SDK的UIKit框架中,Navigation View Controller和TabBar View Controller都是常用的Container View Controller。
那么为什么Unwind Segue需要一个Container View Controllerl的支持?
这里就需要提一下Unwind Segue的设计初衷及其工作方式。之所以引入Unwind Segue,是为了应付任意跳转的情况,即从任意一个View Controller转场到特定的View Controller。在Nib的时代,这种工作往往通过delegate来完成。但是有了Unwind Segue以后,我们只要在需要跳转到的这个特定的View Controller类中实现一个签名为- (IBAction)unwindMethod:(UIStoryboardSegue *)segue
这样的方法即可(其中unwindMethod可以替换为任何你喜欢的名称,但注意,当存在多个这样的方法时,名称不要相同,以免发生冲突,造成不可预料的后果)。这样,我们就可以在任意的View Controller(除了含有这个方法本身的View Controller)通过连接Segue来实现任意View Controller跳转到当前View Controller。不用再多写一行代码,这些都可以通过Interface Builder搞定,非常方便。
Unwind Segue的工作原理大致如下1:
- 当我们通过UI事件或手动调用
performSegueWithIdentifier:sender:
方法触发一个Unwind Segue以后,首先UIKit会发送canPerformUnwindSegueAction:fromViewController:withSender:
消息到sourceViewController
询问是否处理UnwindSegue的action,由于sourceViewController
不能处理(Unwind到自身没有意义),会返回NO - UIKit然后会寻找
sourceViewController
的父Controller。如果sourceController是嵌入Navigation View Controller的子Controller,那么父Controller就是其navigationController - 之后UIKit会发送
viewControllerForUnwindSegueAction:fromViewController:withSender:
消息给navigationController,询问能否找到一个负责处理此action的子Controller - 在navigationController的默认
viewControllerForUnwindSegueAction:fromViewController:withSender:
实现中,navigationController会向自己的navigation栈上的所有子Controller发送canPerformUnwindSegueAction:fromViewController:withSender:
消息。UIViewController
类中,该方法的默认实现会查看unwinde segue action定义是否存在(即上面提到的特定签名的方法是否存在,这个方法的内部实现可以留空),若存在就返回YES。 - 如果navigationController的
viewControllerForUnwindSegueAction:fromViewController:withSender:
方法返回nil,则不会触发任何Unwind Segue - 如果navgationController找到一个子类可以处理Unwind Segue的action,那么UIKit会发送
segueForUnwindingToViewController:fromViewController:identifier:
消息给navigationController,此方法将返回一个实际执行定制转场的segue实例 - 调用
sourceViewController
上的prepareForSegue:sender:
方法 - 调用由
viewControllerForUnwindSegueAction:fromViewController:withSender:
方法返回的destinationViewController
中的Segue action方法 - 调用Unwind Segue实例中的
perform
方法
从上面的我们可以知道,Unwind Segue的正常工作必须要有一个Container View Controller作为所有流程View Controller的父Controller来管理整个流程。在上面的原理说明中,这个父Controller是Navigation View Controller。如果我们要实现一个自己的定义的Container,就必须给自定义的View Controller类实现一些上面提到过的方法:
canPerformUnwindSegueAction:fromViewController:withSender:
viewControllerForUnwindSegueAction:fromViewController:withSender:
segueForUnwindingToViewController:fromViewController:identifier:
关于这些方法的说明和实现方式,我将在下一篇文章中详细讨论。