xcode 如何在不显示中间视图的情况下展开多个视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26785511/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 06:07:03  来源:igfitidea点击:

How to unwind through multiple views without displaying intermediate views

iosobjective-ciphonexcodeuistoryboardsegue

提问by Craig Phares

Assume we have three view controllers: 1, 2, and 3. Using the storyboard, it's pretty simple to unwind from view controller 3 to view controller 1 using an unwind segue. However, when unwinding, view controller 2 is briefly visible before view controller 1 is displayed. Is there any way to get from 3 to 1 without displaying 2 again?

假设我们有三个视图控制器:1、2 和 3。使用故事板,使用展开转场将视图控制器 3 展开到视图控制器 1 非常简单。然而,当展开时,视图控制器 2 在视图控制器 1 显示之前是短暂可见的。有没有办法从 3 到 1 而不再次显示 2?

View Controller 1:

视图控制器 1:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"one did appear");
}

- (IBAction)goToTwo:(id)sender {
  NSLog(@"#### segue to two");
  [self performSegueWithIdentifier:@"TwoSegue" sender:self];
}

- (IBAction)unwindToOne:(UIStoryboardSegue *)sender {
}

View Controller 2:

视图控制器 2:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"two did appear");
}
- (IBAction)goToThree:(id)sender {
  NSLog(@"#### segue to three");
  [self performSegueWithIdentifier:@"ThreeSegue" sender:self];
}

View Controller 3:

视图控制器 3:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"three did appear");
}

- (IBAction)unwindToOne:(id)sender {
  NSLog(@"#### unwind to one");
  [self performSegueWithIdentifier:@"OneSegue" sender:self];
}

This produces the following log messages:

这会产生以下日志消息:

  • one did appear
  • segue to two
  • two did appear
  • segue to three
  • three did appear
  • unwind to one
  • two did appear
  • one did appear
  • 一个确实出现了
  • 转为两个
  • 两个确实出现了
  • 转三
  • 三个确实出现了
  • 放松到一个
  • 两个确实出现了
  • 一个确实出现了

I've tried using custom segues and disabling animation. Although removing animation makes view controller 2 appear for an even shorter period of time, it still appears. Is there any way to program this behavior?

我试过使用自定义转场和禁用动画。虽然移除动画使视图控制器 2 出现的时间更短,但它仍然出现。有没有办法对这种行为进行编程?

Screenshot of storyboard:

故事板截图:

enter image description here

在此处输入图片说明

采纳答案by Craig Phares

Josh's answer led me to a solution. Here's how to accomplish this:

乔希的回答让我找到了解决方案。以下是实现此目的的方法:

Create a root UINavigationController, and assign it to a class that extends UINavigationController and overrides the segueForUnwindingToViewController:fromViewController:identifier method. This could be filtered by the identifier if desired.

创建一个根 UINavigationController,并将其分配给一个扩展 UINavigationController 并覆盖 segueForUnwindingToViewController:fromViewController:identifier 方法的类。如果需要,这可以通过标识符过滤。

CustomNavigationController:

自定义导航控制器:

- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController fromViewController:(UIViewController *)fromViewController identifier:(NSString *)identifier {
  return [[CustomUnwindSegue alloc] initWithIdentifier:identifier source:fromViewController destination:toViewController];
}

Create a custom push segue, that behaves like a modal segue, but utilizes our navigation controller. Use this for all "push" segues.

创建一个自定义推送转场,其行为类似于模态转场,但使用我们的导航控制器。将此用于所有“推”segue。

CustomPushSegue:

CustomPushSegue:

-(void) perform{
  [[self.sourceViewController navigationController] pushViewController:self.destinationViewController animated:NO];
}

Create a custom unwind segue, that uses the navigation controller to pop to the destination. This is called by our navigation controller in the segueForUnwindingToViewController:fromViewController:identifier method.

创建一个自定义的 unwind segue,它使用导航控制器弹出到目的地。这是由我们的导航控制器在 segueForUnwindingToViewController:fromViewController:identifier 方法中调用的。

CustomUnwindSegue:

CustomUnwindSegue:

- (void)perform {
  [[self.destinationViewController navigationController] popToViewController:self.destinationViewController animated:NO];
}

By utilizing a combination of these patterns, the second view controller never appears during the unwind process.

通过使用这些模式的组合,第二个视图控制器在展开过程中永远不会出现。

New log output:

新的日志输出:

  • one did appear
  • #### segue to two
  • two did appear
  • #### segue to three
  • three did appear
  • #### unwind to one
  • one did appear
  • 一个确实出现了
  • ####转为两个
  • 两个确实出现了
  • ####转三
  • 三个确实出现了
  • ####放松到一个
  • 一个确实出现了

I hope this helps someone else with the same issue.

我希望这可以帮助其他有同样问题的人。

回答by Josh Heald

This seems to be due to the way that unwind segues search for the nearest view controller which implements the unwind action you specified in the storyboard. From the documentation:

这似乎是由于 unwind segues 搜索最近的视图控制器的方式,该控制器实现了您在故事板中指定的 unwind 操作。从文档

How an Unwind Segue Selects its Destination

When an unwind segue is triggered, it must locate the nearest view controller that implements the unwind action specified when the unwind segue was defined. This view controller becomes the destination of the unwind segue. If no suitable view controller is found, the unwind segue is aborted. The search order is as follows:

A viewControllerForUnwindSegueAction:fromViewController:withSender:message is sent to the parent of the source view controller.

A viewControllerForUnwindSegueAction:fromViewController:withSender:message is sent to the next parent view controller [...]

You can override canPerformUnwindSegueAction:fromViewController:withSender: if you have specific requirements for whether your view controller should handle an unwind action.

Unwind Segue 如何选择目的地

当一个 unwind segue 被触发时,它必须找到最近的视图控制器,该控制器实现了在定义 unwind segue 时指定的 unwind 操作。这个视图控制器成为 unwind segue 的目的地。如果没有找到合适的视图控制器,展开转场将被中止。搜索顺序如下:

一条viewControllerForUnwindSegueAction:fromViewController:withSender:消息被发送到源视图控制器的父级。

一条viewControllerForUnwindSegueAction:fromViewController:withSender:消息被发送到下一个父视图控制器 [...]

您可以覆盖 canPerformUnwindSegueAction:fromViewController:withSender: 如果您对视图控制器是否应处理展开操作有特定要求。

The view controllers in your example don't have a parent, so it seemsthat the fallback (which I can't see documentation for) is to instantiate each presentingViewControllerand call canPerformUnwindSegueActionon them in turn. Returning NOfrom this method in ViewControllerTwodoesn't prevent its instantiation and display, so that doesn't solve the issue.

您示例中的视图控制器没有父级,因此似乎后备(我看不到文档)是实例化每个presentingViewControllercanPerformUnwindSegueAction依次调用它们。NO从这个方法返回ViewControllerTwo并不会阻止它的实例化和显示,所以这不能解决问题。

I've tried your code embedded within a navigation controller, and it works fine. This is because in that case, UINavigationControlleris the parent of each of your view controllers, and it handles all the selection of a destination view controller. More documentation:

我已经尝试将您的代码嵌入到导航控制器中,并且运行良好。这是因为在这种情况下,它UINavigationController是每个视图控制器的父级,它处理目标视图控制器的所有选择。更多文档:

Container View Controllers

Container view controllers have two responsibilities in the unwind process, both discussed below. If you are using the container view controllers provided by the SDK, such as UINavigationController, these are handled automatically.

容器视图控制器

容器视图控制器在 unwind 过程中有两个职责,都在下面讨论。如果您使用的是 SDK 提供的容器视图控制器,例如 UINavigationController,这些会自动处理。

If you were to create a simple container view controller to act as the parent for your three view controllers, you could use its viewControllerForUnwindSegueActionmethod to check each of its child controllers for the existence of the unwind action, before calling canPerformUnwindSegueActionon that controller, and finally returning the first one of those which returns YES.

如果要创建一个简单的容器视图控制器来充当三个视图控制器的父级,则可以使用其viewControllerForUnwindSegueAction方法检查其每个子控制器是否存在展开操作,然后调用canPerformUnwindSegueAction该控制器,最后返回第一个返回YES.

Selecting a Child View Controller to Handle An Unwind Action

As mentioned in How an Unwind Segue Selects its Destination, the source view controller of an unwind segue defers to its parent to locate a view controller that wants to handle the unwind action. Your container view controller should override the method shown in Listing 2 to search its child view controllers for a view controller that wants to handle the unwind action. If none of a container's child view controllers want to handle the unwind action, it should invoke the super's implementation and return the result.

A container view controller should search its children in a way that makes sense for that container. For example, UINavigationController searches backwards through its viewControllers array, starting from the view at the top of the navigation stack.

Listing 2 Override this method to return a view controller that wants to handle the unwind action. - (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender

选择一个子视图控制器来处理一个展开动作

如 Unwind Segue 如何选择其目标中所述,unwind segue 的源视图控制器遵循其父级来定位想要处理 unwind 操作的视图控制器。您的容器视图控制器应覆盖清单 2 中所示的方法,以在其子视图控制器中搜索想要处理展开操作的视图控制器。如果容器的子视图控制器都不想处理展开操作,则它应该调用超级的实现并返回结果。

容器视图控制器应该以对该容器有意义的方式搜索其子项。例如, UINavigationController 从导航堆栈顶部的视图开始,在其 viewControllers 数组中向后搜索。

清单 2 重写此方法以返回一个想要处理展开操作的视图控制器。- (UIViewController *)viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender

Container view controller design has a whole articlededicated to it by Apple, which I won't duplicate here (more than enough of Apple's writing in this answer already!) but it looks like it will take some thought to get it right, as it depends on the exact role you want these to play in your application.

Container view controller design 有一整篇Apple 专门针对它的文章,我不会在这里重复(Apple 在这个答案中的写作已经绰绰有余了!)取决于您希望它们在您的应用程序中发挥的确切作用。

A quick workaround, to get your desired behaviour using unwind segues, could be to embed the view controllers in a UINavigationController, and then hide the navigation bar using

一个快速的解决方法,使用 unwind segues 来获得你想要的行为,可以是将视图控制器嵌入到 中UINavigationController,然后使用隐藏导航栏

[self.navigationController setNavigationBarHidden:YES];

回答by Sandy Chapman

Looks like custom segues are being used, so it's possible that's interfering somehow, although I've never seen it happn. I suggest you check out Apple's example project. It also has custom segues in it so it should serve as a good starting point for you.

看起来正在使用自定义转场,因此可能会以某种方式进行干扰,尽管我从未见过它发生。我建议您查看 Apple 的示例项目。它也有自定义的转场,所以它应该是你一个很好的起点。

Apple's Unwind Segue Example

Apple 的 Unwind Segue 示例

回答by danh

I surprised you're seeing the behavior you're seeing, but one way to change it would be to use an explicit dismiss rather than unwind segues (this assumes the forward segues are modal).

我很惊讶您看到了您所看到的行为,但是改变它的一种方法是使用显式关闭而不是展开转场(这假设前转转场是模态的)。

Everything will look right if VC1 does this:

如果 VC1 这样做,一切都会看起来正确:

[self dismissViewControllerAnimated:YES completion:^{}];

Or if some other vc does this:

或者,如果其他一些 vc 这样做:

[vc1 dismissViewControllerAnimated:YES completion:^{}];

The only hitch is that you'll need a handle to vc1 if you want to dismiss from some other vc. You could use a delegate pattern to let vc1 know it should do the dismiss, but a simpler solution is to have vc2 or 3 post a notification when the unwind should happen.

唯一的问题是,如果您想从其他 vc 中解散,则需要 vc1 的句柄。您可以使用委托模式让 vc1 知道它应该解除,但更简单的解决方案是让 vc2 或 3 在应该发生放松时发布通知。

VCs 2 or 3 can do this:

VC 2 或 3 可以这样做:

// whenever we want to dismiss
[[NSNotificationCenter defaultCenter] postNotificationName:@"TimeToDismiss" object:self];

And VC1 can do this:

而 VC1 可以做到这一点:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(doDismiss:)
                                             name:@"TimeToDismiss"
                                           object:nil];

- (void)doDismiss:(NSNotification *)notification {
    [self dismissViewControllerAnimated:YES completion:^{}];
}