而不是push segue如何替换视图控制器(或从导航堆栈中删除)?

Ale*_*ber 64 iphone objective-c ios segue uistoryboardsegue

我有一个小的iPhone应用程序,它使用导航控制器显示3个视图(这里全屏):

Xcode截图

首先,它显示社交网络列表(Facebook,Google +等):

列表截图

然后它会显示一个OAuth对话框,询问凭据:

登录截图

和(之后,同样UIWebView)权限:

权限截图

最后,它显示了具有用户详细信息的最后一个视图控制器(在真实应用程序中,这将是可以启动多人游戏的菜单):

详情截图

这一切都运作良好,但我有一个问题,当用户想要返回并选择另一个社交网络时:

用户触摸后退按钮而不显示第一个视图,显示第二个视图,再次询问OAuth凭据/权限.

我能在这做什么?Xcode 5.0.2显示了一个非常有限的segues选择 - 推送,模态(我无法使用,因为它模糊了我的游戏所需的导航栏)和自定义.

我是一个iOS编程新手,但早些时候我开发了一个Adobe AIR移动应用程序,并且可以1)替换视图而不是推送2)从导航堆栈中删除不需要的视图.

如何在原生应用程序中做同样的事情?

ima*_*747 59

为了扩展上面的各种细分,这是我的解决方案.它具有以下优点:

  • 可以在视图堆栈中的任何位置工作,而不仅仅是顶视图(不确定这是否真实需要或甚至在技术上可能触发,但嘿它在那里).
  • 在显示替换之前,它不会导致弹出OR转换到前一个视图控制器,它只显示具有自然转换的新控制器,后向导航到源控制器的相同后退导航.

Segue Code:

- (void)perform {
    // Grab Variables for readability
    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    UIViewController *destinationController = (UIViewController*)[self destinationViewController];
    UINavigationController *navigationController = sourceViewController.navigationController;

    // Get a changeable copy of the stack
    NSMutableArray *controllerStack = [NSMutableArray arrayWithArray:navigationController.viewControllers];
    // Replace the source controller with the destination controller, wherever the source may be
    [controllerStack replaceObjectAtIndex:[controllerStack indexOfObject:sourceViewController] withObject:destinationController];

    // Assign the updated stack with animation
    [navigationController setViewControllers:controllerStack animated:YES];
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ele 37

您可以使用自定义segue:要做到这一点,您需要创建一个子类UIStoryboardSegue(示例MyCustomSegue)的类,然后您可以用这样的东西覆盖"perform"

-(void)perform {
    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    UIViewController *destinationController = (UIViewController*)[self destinationViewController];
    UINavigationController *navigationController = sourceViewController.navigationController;
    // Pop to root view controller (not animated) before pushing
    [navigationController popToRootViewControllerAnimated:NO];
    [navigationController pushViewController:destinationController animated:YES];    
}
Run Code Online (Sandbox Code Playgroud)

此时转到Interface Builder,选择"custom"segue,并输入您的类的名称(示例MyCustomSegue)


chr*_*ton 24

自定义segue对我不起作用,因为我有一个Splash视图控制器,我想替换它.由于列表中只有一个视图控制器,因此popToRootViewController仍然将Splash留在堆栈中.我使用以下代码替换单个控制器

-(void)perform {
    UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
    UIViewController *destinationController = (UIViewController*)[self destinationViewController];
    UINavigationController *navigationController = sourceViewController.navigationController;
    [navigationController setViewControllers:@[destinationController] animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

现在在Swift 4中:

class ReplaceSegue: UIStoryboardSegue {

    override func perform() {
        source.navigationController?.setViewControllers([destination], animated: true)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在在Swift 2.0中

class ReplaceSegue: UIStoryboardSegue {

    override func perform() {
        sourceViewController.navigationController?.setViewControllers([destinationViewController], animated: true)
    }
}
Run Code Online (Sandbox Code Playgroud)


Dr.*_*gos 17

快速2版的ima747回答:

override func perform() {
    let navigationController: UINavigationController = sourceViewController.navigationController!;

    var controllerStack = navigationController.viewControllers;
    let index = controllerStack.indexOf(sourceViewController);
    controllerStack[index!] = destinationViewController

    navigationController.setViewControllers(controllerStack, animated: true);
}
Run Code Online (Sandbox Code Playgroud)

正如他所提到的,它具有以下优点:

  • 可以在视图堆栈中的任何位置工作,而不仅仅是顶视图(不确定这是否真实需要或甚至在技术上可能触发,但嘿它在那里).
  • 在显示替换之前,它不会导致弹出OR转换到前一个视图控制器,它只显示具有自然转换的新控制器,后向导航到源控制器的相同后退导航.

  • 为了使它更安全,你可以在`if let`条件中获取索引,这样你就不需要强行解包变量,这可能容易出错. (6认同)

小智 16

对于这个问题,我认为答案很简单

  1. 从NavigationController获取视图控制器数组
  2. 删除最后一个ViewController(当前视图控制器)
  3. 最后插入一个新的
  4. 然后将ViewControllers数组设置为如下所示的navigationController:

     if let navController = self.navigationController {
        let newVC = DestinationViewController(nibName: "DestinationViewController", bundle: nil)
    
        var stack = navController.viewControllers
        stack.remove(at: stack.count - 1)       // remove current VC
        stack.insert(newVC, at: stack.count) // add the new one
        navController.setViewControllers(stack, animated: true) // boom!
     }
    
    Run Code Online (Sandbox Code Playgroud)

Swift 3完美配合.

希望它有助于一些新人.

干杯.


Les*_*ter 9

使用展开segue将是解决此问题的最合适的解决方案.我同意劳罗.

以下是从detailsViewController [或viewController3]到myAuthViewController [或viewController1]设置展开segue的简要说明

这基本上就是你如何通过代码执行unwind segue.

  • 在要解除的viewController中实现IBAction方法(在本例中为viewController1).方法名称可以是任何长的,它需要一个UIStoryboardSegue类型的参数.

    @IBAction func unwindToMyAuth(segue: UIStoryboardSegue) {
    println("segue with ID: %@", segue.Identifier)
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 在要从其中展开的viewController(3)中链接此方法.要链接,请右键单击(双指点击)viewController顶部的退出图标,此时"unwindToMyAuth"方法将显示在弹出框中.控制单击此方法到第一个图标,viewController图标(也出现在viewController的顶部,与退出图标位于同一行).选择弹出的"手动"选项.

  • 在Document outline中,对于相同的视图(viewController3),选择刚刚创建的展开segue.转到属性检查器并为此展开segue分配唯一标识符.我们现在有一个通用的展开segue准备使用.

  • 现在,unwind segue可以像代码中的任何其他segue一样执行.

    performSegueWithIdentifier("unwind.to.myauth", sender: nil)
    
    Run Code Online (Sandbox Code Playgroud)

这种方法将使您从viewController3转到viewController1,而无需从导航层次结构中删除viewController2.

与其他segue不同,展开segue不会实例化视图控制器,它们只会转到导航层次结构中的现有视图控制器.


Ale*_*bin 5

正如之前的回答中提到的那样,弹出不动画然后推送动画看起来不太好,因为用户会看到实际的过程。我建议你先推送动画,然后删除之前的vc。像这样:

extension UINavigationController {
    func replaceCurrentViewController(with viewController: UIViewController, animated: Bool) {
        pushViewController(viewController, animated: animated)
        let indexToRemove = viewControllers.count - 2
        if indexToRemove >= 0 {
            viewControllers.remove(at: indexToRemove)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Hix*_*eld 5

这是一个快速的 Swift 4/5 解决方案,通过创建一个自定义序列,用新的(无动画)替换(顶部堆栈)视图控制器:

class SegueNavigationReplaceTop: UIStoryboardSegue {

    override func perform () {
        guard let navigationController = source.navigationController else { return }
        navigationController.popViewController(animated: false)
        navigationController.pushViewController(destination, animated: false)
    }
}
Run Code Online (Sandbox Code Playgroud)