Unwind Segues是什么,如何使用它们?

606

iOS 6和Xcode 4.5具有一项称为“取消Segue”的新功能:

取消Segue可以允许转换到故事板中现有场景的实例

除了Xcode 4.5的发布说明中对此的简要介绍外,UIViewController现在似乎还有一些新方法:

- (BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier

什么是 unwind segue?它们的工作原理以及可以用于什么?

6个回答

1289

简而言之

解环(unwind segue)(有时也称为退出环(segue))可用于通过推送、模态或弹出的转场方式向后导航(就像从导航栏中弹出导航项,关闭弹出窗口或关闭模态呈现的视图控制器一样)。此外,您实际上可以通过一系列推送/模态/弹出转场步骤进行解环,例如:使用单个解环操作“返回”导航层次结构中的多个步骤。

当执行解环转场时,您需要指定一个操作,这是您希望解环到达的视图控制器的动作方法。

Objective-C:

- (IBAction)unwindToThisViewController:(UIStoryboardSegue *)unwindSegue
{
}

Swift:

@IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}
该操作方法的名称在创建Storyboard中的Unwind Segue时使用。此外,在执行Unwind Segue之前,该方法会被调用。您可以从传递的UIStoryboardSegue参数中获取源视图控制器以与启动Segue的视图控制器进行交互(例如,获取模态视图控制器的属性值)。在这方面,该方法具有类似UIViewController的prepareForSegue:方法的功能。
iOS 8更新:Unwind Segue也适用于iOS 8的自适应Segue,例如Show和Show Detail。
一个例子:
让我们拥有一个故事板,其中包含一个导航控制器和三个子视图控制器。
从绿色视图控制器,您可以跳转回红色视图控制器。从蓝色视图控制器,您可以通过绿色视图控制器跳转回红色视图控制器或直接跳转回红色视图控制器。要启用退回功能,必须将特殊的操作方法添加到红色和绿色视图控制器中。以下是Red中的操作方法:
Objective-C:
@implementation RedViewController

- (IBAction)unwindToRed:(UIStoryboardSegue *)unwindSegue
{
}

@end

Swift:

@IBAction func unwindToRed(segue: UIStoryboardSegue) {
}

在添加操作方法后,您可以通过控制拖动到Exit图标,在Storyboard中定义unwind segue。在此示例中,当按下按钮时,我们希望从绿色返回到红色:

enter image description here

您必须选择在要返回的视图控制器中定义的操作:

enter image description here

您也可以从“导航堆栈”中“两步之遥”的蓝色返回到红色。关键是选择正确的unwind action。

在执行unwind segue之前,将调用操作方法。在本例中,我定义了一个从绿色和蓝色返回到红色的unwind segue。我们可以通过UIStoryboardSegue参数在操作方法中访问unwind的源:

Objective-C:

- (IBAction)unwindToRed:(UIStoryboardSegue *)unwindSegue
{
    UIViewController* sourceViewController = unwindSegue.sourceViewController;

    if ([sourceViewController isKindOfClass:[BlueViewController class]])
    {
        NSLog(@"Coming from BLUE!");
    }
    else if ([sourceViewController isKindOfClass:[GreenViewController class]])
    {
        NSLog(@"Coming from GREEN!");
    }
}

Swift:

@IBAction func unwindToRed(unwindSegue: UIStoryboardSegue) {
    if let blueViewController = unwindSegue.sourceViewController as? BlueViewController {
        println("Coming from BLUE")
    }
    else if let redViewController = unwindSegue.sourceViewController as? RedViewController {
        println("Coming from RED")
    }
}

使用推进/模态过渡的组合也可以进行反向跳转。例如,如果我添加了另一个黄色视图控制器并使用模态跳转,我们就可以在单个步骤中从黄色返回到红色:

enter image description here

从代码中反向跳转

当您通过将某些内容拖动到视图控制器的“退出”符号来定义反向跳转时,文档大纲中会出现一个新的segue:

enter image description here

选择该segue并进入属性检查器,可以看到“标识符”属性。使用此属性为您的segue命名:

enter image description here

完成后,就可以像执行其他segue一样从代码中执行反向segue:

Objective-C:

[self performSegueWithIdentifier:@"UnwindToRedSegueID" sender:self];

Swift:

performSegueWithIdentifier("UnwindToRedSegueID", sender: self)

13
+1 优秀的答案。它们听起来非常好,但是像dismissViewControllerAnimated:completion:popViewControllerAnimated:这样的方法不能实现相同的效果吗? - Sam Spencer
33
可以。然而,如果您正在使用故事板,解开退场可以经常使用更少的代码实现相同的效果。实际上,现在您可以在不编写任何代码的情况下关闭模态呈现的视图控制器。当然,在许多情况下,从代码中关闭控制器仍然是正确的做法。 - Imre Kelényi
7
请确保将您的操作方法添加到头文件中,否则Storyboard将无法识别。 - Kyle C
18
dismissViewControllerAnimated:completion:popViewControllerAnimated: 相比,另一个优势是你所添加到要解除的视图控制器中的方法会被调用,因此你有一种简单的方式来知道所呈现的视图控制器已经完成,而无需将呈现视图控制器设置为所呈现视图控制器的委托。 - honus
8
我能否提议稍微修改一下?你在RedViewController.m中加入了-(IBAction)unwindTRed:(UIStoryboardSegue *)unwindSegue,但这并不是完全“显而易见”的,同时它在任何故事板的绿色退出按钮中都可以使用。非常棒的答案,现在我会用它来解决其他问题。谢谢! - John Ballinger
显示剩余16条评论

169

关于如何在StoryBoard中使用unwind segue...

步骤1)

进入你要进行“返回”操作的视图控制器的代码页面,添加以下内容:

Objective-C

- (IBAction)unwindToViewControllerNameHere:(UIStoryboardSegue *)segue {
    //nothing goes here
}

在 Obj-C 中,确保你也在 .h 文件中声明了这个方法。

Swift

@IBAction func unwindToViewControllerNameHere(segue: UIStoryboardSegue) {
    //nothing goes here
}

第二步)

在故事板中,找到您想要从中解开的视图,然后从您的按钮或其他控件直接拖动一个转场动画到源视图右上角的小橙色 "EXIT" 图标。

enter image description here

现在应该有一个选项可以连接到“- unwindToViewControllerNameHere”

这就是全部步骤,当你点击按钮时,你的转场动画将会被解开。


5
我发现在Xcode 4.5及更早的版本中,必须在头文件中声明IBAction。我不知道现在是否仍然如此。 - Steven Fisher
2
有没有办法在不使用Storyboard的情况下完成第2步,即以编程方式?我的Storyboard(界面构建器)出了问题,它不显示反向连线(Xcode中的错误)。 - Van Du Tran

46

Unwind Segues(回 unwind 转)用于从经过一系列 Segue 到达“当前”视图控制器的某个视图控制器“返回”。

假设你有一个名为 MyNavController 的导航控制器,以 A 作为其根视图控制器。现在你使用 push Segue 转到 B。此时导航控制器的 viewControllers 数组中包含 A 和 B,并且 B 可见。现在你以模态方式呈现 C

使用 unwind segues,你现在可以从 C 回退到 B(即解除模态呈现的视图控制器),基本上是“撤销”模态 segue。你甚至可以一直回退到根视图控制器 A,撤销模态 segue 和 push segue。

Unwind Segues 让回溯变得简单。例如,在 iOS 6之前,取消呈现的视图控制器的最佳实践是将呈现视图控制器设置为呈现视图控制器的委托,然后调用自定义委托方法,该方法然后解除呈现的视图控制器。听起来麻烦和复杂吗?是的,所以 unwind segues 很方便。


7
你可以从被呈现的控制器中调用 dismissViewController:animated。你不必委托其他对象来完成这个动作。但是如果你需要传递数据回去,那么你需要使用委托或者其他方法。 - mxcl
3
尽管你可以从呈现的控制器中调用 dismissViewController:animated:,但“最佳实践”确实是调用呈现控制器上的委托方法来为你执行此操作,正如 Yang 所提到的。 - Chris Nolet

37

在其他答案中没有提到的一点是,当你不知道初始segue来源时如何处理取消操作,而这对我来说甚至更为重要。例如,假设你有一个帮助视图控制器(H), 你从两个不同的视图控制器(AB)中以模态方式显示它:

AH
BH

你如何设置取消segue以便返回到正确的视图控制器? 答案是,在AB中声明一个带有相同名称的取消操作,例如:

// put in AViewController.swift and BViewController.swift
@IBAction func unwindFromHelp(sender: UIStoryboardSegue) {
    // empty
}

这样,反向传递将找到触发segue的任何视图控制器(AB)并返回到它。

换句话说,将反向传递操作看作描述segue是从哪里来的,而不是它要去哪里。


2
谢谢您提供这个信息,我一直在寻找它。 - Madhu
2
很棒能提到这些信息,因为我已经实现了解决方案,直到在这里得到了您的提示之前,什么都没有发生,非常感谢您的支持。 - Amr Angry
这是一条很棒的信息!非常感谢! - Dominique Vial

24

Swift iOS:

步骤1:将此方法定义到您想要返回的主控制器视图中。

//pragma mark - Unwind Seques
@IBAction func goToSideMenu(segue: UIStoryboardSegue) {

    println("Called goToSideMenu: unwind action")

}

步骤2:(故事板)右键单击您的SLAVE/CHILD EXIT按钮,并选择“goToSideMenu”作为操作,将其连接到您将单击以返回到MASTER控制器视图的按钮上:

enter image description here步骤3:构建和运行...


1
例如,如果您从viewControllerB导航到viewControllerA,则在您的viewControllerA中,下面的委托将调用并共享数据。
@IBAction func unWindSeague (_ sender : UIStoryboardSegue) {
        if sender.source is ViewControllerB  {
            if let _ = sender.source as? ViewControllerB {
                self.textLabel.text = "Came from B = B->A , B exited"
            }
            
        }

}
  • 取消Segue源视图控制器(您需要将Exit按钮连接到VC的退出图标并将其连接到unwindseague):

enter image description here

  • 取消跳转完成 -> viewControllerA 的 TextLabel 已更改。

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接