【问题标题】:Custom segue to push a specific UIViewController to UINavigationController自定义 segue 将特定的 UIViewController 推送到 UINavigationController
【发布时间】:2013-04-21 16:06:19
【问题描述】:

我想创建一个自定义 segue,其作用方式与在 UINavigationController 视图控制器上使用时的标准推送 segue 相同。我已经实现了我的自定义 segue:

CustomSegue.m

    -(void)perform {
    UIViewController *source = (UIViewController *)[self sourceViewController];
    UIViewController *dest =(UIViewController *)[self destinationViewController];
    if (1==2 //test) {
        [source.navigationController pushViewController:dest animated:YES];
    }
    else {
        UIViewController *altDest = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL]
         instantiateViewControllerWithIdentifier:@"alternateView"];
        [source.navigationController pushViewController:altDest animated:YES];
    }

如您所见,我想使用自定义推送 segue 的原因是我可以根据用户配置决定推送哪个视图控制器(目前只检查一个简单的 1==2 表达式)。我可以毫无问题地实例化备用视图控制器,但我想要做的是来回走动,而无需每次都重新加载视图控制器(使用后退和下一步按钮)。有没有办法从情节提要中检索现有实例,或者一些标准的方法?

【问题讨论】:

    标签: iphone ios uinavigationcontroller uistoryboard


    【解决方案1】:

    代替带有perform 的自定义segue,执行您所描述的方式,即实时选择是推送dest 还是altDest,要么是(1)根本不使用segue,并且直接调用pushViewController,就像你在这里做的那样,或者(2)准备两个从视图控制器发出的segues作为一个整体,然后调用performSegueWithIdentifier:来说明我们应该执行哪个。

    至于直接从destaltDest,您可以将altDest 压在dest 之上,然后从导航控制器的视图控制器堆栈中删除dest

    就像 iOS 一样,如果您根本不使用情节提要,这一切都会变得更加容易和明显。这就是我不喜欢故事板的原因:它们头脑简单且局限,而且会分散人们对 iOS 真正工作方式的注意力。

    【讨论】:

    • 这是我书中关于如何完全用代码操作导航界面的讨论:apeth.com/iOSBook/…
    • 谢谢!设法成功地让这个工作与你和@rdelmar 的答案一起工作。我已将“下一步”按钮操作更改为标准 IBAction,以推送所需的视图控制器而不是使用 segues - 这样我可以将实例化的视图控制器存储在堆栈中的前一个视图控制器中,并在它推送实例化的视图控制器时存在。我同意故事板掩盖了很多正在发生的事情,这导致在较低层次上缺乏理解。当它允许我时,我会将您的答案标记为已接受:)
    • 您可能想尝试使用setViewControllers:animated:。它可以让你重新排列现有的视图控制器可能添加更多的视图控制器,删除现有的视图控制器等,并且它执行一个自动动画(除非你告诉它不要)。因此,您可以将两个备用视图控制器保留在堆栈中,并继续重新排列它们。
    • @matt 我有类似的用例。但我很困惑我怎么可能执行以下操作:将 altDest 推到 dest 之上,然后从导航控制器的视图控制器堆栈中删除 dest
    【解决方案2】:

    没有办法从情节提要中检索现有的控制器——如果有一个 controllerWithIdentifier: 方法来做到这一点,那就太好了,但没有。 Segues(除了展开)总是实例化新的控制器,所以我认为你不能用 segue 做你想做的事。如果您想多次前进(推送)到同一个控制器,那么您需要在代码中创建一个指向您的控制器的属性,并在推送到它之前检查该控制器是否存在。

    【讨论】:

      【解决方案3】:

      正如其他人所指出的,您不能使用 segue 推送到控制器的现有实例。执行 segue 的过程总是会为您创建一个目标控制器的新实例。

      就个人而言,当我在视图控制器的现有实例之间跳转时,我认为是“容器视图控制器”,例如 UIPageViewController,这使得在两个或多个控制器之间转换非常容易,而不必每次都重新实例化它们时间。

      如果您不喜欢页面视图控制器施加的约束(例如,您可能不喜欢 iOS 5 版本仅支持页面卷曲过渡,或者 iOS 6 仅添加滚动过渡的事实,并且您想要一些东西否则),然后你会做一个自定义容器视图控制器。

      例如,如果我想在两个视图控制器之间跳转而不是每次都重新实例化它们,我会首先创建一个自定义容器视图控制器,即“父级”,并确保我有一个属性来跟踪哪个我目前所在的孩子:

      @property (nonatomic) NSInteger childViewIndex;
      

      如果仅支持 iOS 6.0 及更高版本,我会在父视图控制器的场景中添加一个“容器视图”。如果支持 6.0 之前的 iOS 版本,我会在场景中添加一个标准的UIView,然后手动实例化第一个子控制器:

      - (void)viewDidAppear:(BOOL)animated
      {
          [super viewDidAppear:animated];
      
          UIViewController *controller;
      
          // add the first child
      
          UIViewController *controller = [self addChildWithIdentifier:@"One"];
          [self.containerView addSubview:controller.view];
          [controller didMoveToParentViewController:self];
          self.childViewIndex = 0;
      }
      
      - (UIViewController *)addChildWithIdentifier:(NSString *)storyboardIdentifier
      {
          UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:storyboardIdentifier];
          [self addChildViewController:controller];
          controller.view.frame = self.containerView.bounds;
          controller.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
      
          return controller;
      }
      

      然后,当我想转换到第二个孩子(或转换回第一个孩子)时,我会在父视图控制器中调用以下例程:

      - (void)transitionToViewControllerIndex:(NSInteger)index
      {
          // don't do anything if we're trying to transition to ourselves!
      
          if (index == self.childViewIndex)
              return;
      
          // identify the two controllers in question
      
          UIViewController *sourceController = self.childViewControllers[self.childViewIndex];
          UIViewController *destinationController;
      
          // if we're asking for page 2, but we only have one defined, then we'll have to instantiate it
      
          BOOL instantiateDestination = (index == 1 && [self.childViewControllers count] < 2);
      
          if (instantiateDestination)
              destinationController = [self addChildWithIdentifier:@"Two"];
          else
              destinationController = self.childViewControllers[index];
      
          // configure the destination controller's frame
      
          destinationController.view.frame = sourceController.view.frame;
      
          // if you're jumping back and forth, set the animation appropriate for the
          // direction we're going
      
          UIViewAnimationOptions options;
      
          if (index > self.childViewIndex)
          {
              options = UIViewAnimationOptionTransitionFlipFromRight;
          }
          else
          {
              options = UIViewAnimationOptionTransitionFlipFromLeft;
          }
      
          // now transition to that destination controller
      
          [self transitionFromViewController:sourceController
                            toViewController:destinationController
                                    duration:0.5
                                     options:options
                                  animations:^{
                                      // for simple flip, you don't need anything here,
                                      // but docs say this can't be NULL; if you wanted
                                      // to do some other, custom annotation, you'd do it here
                                  }
                                  completion:^(BOOL finished) {
                                      if (instantiateDestination)
                                          [destinationController didMoveToParentViewController:self];
                                  }];
      
          self.childViewIndex = index;
      }
      

      因此,要转换到第二个子视图控制器,您可以简单地调用:

      [self transitionToViewControllerIndex:1];
      

      如果你想转回来,你可以打电话:

      [self transitionToViewControllerIndex:0];
      

      我在这里只是触及表面,但容器视图控制器(或者如果没有标准的控制器为您完成这项工作,自定义容器视图控制器)正是您所需要的。

      有关详细信息,请参阅:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-27
        • 2017-07-26
        • 2012-02-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多