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

时间:2014-11-06 17:20:03

标签: ios objective-c iphone xcode uistoryboardsegue

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

查看控制器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 {
}

查看控制器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];
}

查看控制器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];
}

这会产生以下日志消息:

  • 一个确实出现了
  • segue to two
  • 两个确实出现了
  • segue to three
  • 三个确实出现了
  • 放松到一个
  • 两个确实出现了
  • 一个确实出现了

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

故事板的屏幕截图:

enter image description here

4 个答案:

答案 0 :(得分:5)

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

  

Unwind Segue如何选择目的地

     

当触发展开segue时,它必须找到最近的视图控制器,该控制器实现在定义展开segue时指定的展开动作。此视图控制器成为展开segue的目标。如果找不到合适的视图控制器,则取消展开segue。搜索顺序如下:

     

viewControllerForUnwindSegueAction:fromViewController:withSender:消息发送给源视图控制器的父级。

     

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

     

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

您的示例中的视图控制器没有父级,因此似乎后备(我无法查看文档)是实例化每个{{1然后依次调用presentingViewController。在canPerformUnwindSegueAction中从此方法返回NO并不会阻止其实例化和显示,因此无法解决问题。

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

  

容器视图控制器

     

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

如果您要创建一个简单的容器视图控制器作为三个视图控制器的父级,您可以使用其UINavigationController方法检查其每个子控制器是否存在展开操作,之前在该控制器上调用viewControllerForUnwindSegueAction,最后返回返回canPerformUnwindSegueAction的第一个。

  

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

     

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

     

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

     

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

容器视图控制器设计有一个专用于它的whole article,我在这里不会复制(已经足够苹果在这个答案中写了!)但它看起来像它需要一些思考才能做到正确,因为它取决于您希望这些在您的应用程序中扮演的确切角色。

使用展开segue获得所需行为的快速解决方法可能是将视图控制器嵌入YES,然后使用

隐藏导航栏
UINavigationController

答案 1 :(得分:4)

Josh的回答让我找到了解决方案。以下是如何实现这一目标:

创建一个根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];
}

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

<强> CustomPushSegue:

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

创建自定义展开segue,使用导航控制器弹出到目标。我们的导航控制器在segueForUnwindingToViewController中调用它:fromViewController:identifier方法。

<强> CustomUnwindSegue:

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

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

新日志输出:

  • 一个确实出现了
  • #### segue to two
  • 两个确实出现了
  • #### segue to three
  • 三个确实出现了
  • ####放松到一个
  • 一个确实出现了

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

答案 2 :(得分:0)

看起来正在使用自定义segues,因此可能会以某种方式干扰,尽管我从未见过它很开心。我建议你查看Apple的示例项目。它还有自定义区域,因此它应该是一个很好的起点。

Apple's Unwind Segue Example

答案 3 :(得分:0)

我很惊讶你看到了你所看到的行为,但改变它的一种方法是使用明确的解除而不是展开segues(这假设前向segues是模态的)。

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

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

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

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

唯一的障碍是,如果你想解雇其他vc,你需要vc1的句柄。您可以使用委托模式让vc1知道它应该执行解雇,但更简单的解决方案是让vc2或3在发生解除时发布通知。

VC 2或3可以做到这一点:

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

VC1可以做到这一点:

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

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