Skip/Add 导航堆栈中的视图控制器

Skip/Add a View Controller in Navigation Stack

我在导航控制器中嵌入了三个视图控制器。我想从 VC1 转到 VC3,这样在 VC3 中,导航项的后退按钮会将用户定向到 VC2 而不是 VC1。我认为这应该通过在创建 VC3 时 将 VC2 添加到 VC1 和 VC3 之间的导航堆栈 跳过第二个视图控制器 来完成.

我试过这个:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let identifier = segue.identifier {
        switch identifier {
        case "JumpToThirdVCSegue":
            if let secondvc = segue.destinationViewController as? SecondViewController {
                secondvc.performSegueWithIdentifier("ThirdVCSegue", sender: self)
            }
        default: break
        }
    }
}

但我无法让它工作。也许在 View Controller 尚未打开时无法执行 segue?

跳过视图控制器/在导航堆栈中间添加视图控制器的最佳方法是什么?我希望这可以帮助您理解我正在尝试做的事情:

像这样的东西应该可以工作:

    self.navigationController?.pushViewController(viewController2, animated: true)
    self.navigationController?.pushViewController(viewController3, animated: true)

编辑:

如果你想在不被用户注意到的情况下推送第二个视图控制器,你需要在推送第三个视图控制器后将其添加到导航控制器。这可以通过实施 UINavigationControllerDelegate 来完成。您可以将第二个视图控制器存储在一个变量中,并将其插入到委托方法中的导航控制器层次结构中。您的主视图控制器将如下所示:

class MyViewController: UIViewController, UINavigationControllerDelegate {

    var viewControllerToInsertBelow : UIViewController?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.delegate = self
    }

    func pushTwoViewControllers() {
        if let viewController2 = self.storyboard?.instantiateViewControllerWithIdentifier("id1"),
            let viewController3 = self.storyboard?.instantiateViewControllerWithIdentifier("id2") { //change this to your identifiers
                self.viewControllerToInsertBelow = viewController2
                self.navigationController?.pushViewController(viewController3, animated: true)
        }
    }

    //MARK: - UINavigationControllerDelegate
    func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
        if let vc = viewControllerToInsertBelow {
            viewControllerToInsertBelow = nil
            let index = navigationController.viewControllers.indexOf(viewController)!
            navigationController.viewControllers.insert(vc, atIndex: index)
        }
    }

}

编辑:使用 swift 和 segues,这应该有效:

override func performSegueWithIdentifier(identifier: String?, sender: AnyObject?) {
    super.performSegueWithIdentifier(identifier, sender: sender);

    if identifier == "JumpToThirdVCSegue" {
        // now the vc3 was already pushed on the navigationStack
        var navStackArray : [AnyObject]! = self.navigationController!.viewControllers
        // insert vc2 at second last position
        navStackArray.insert(viewController2, atIndex: navStackArray.count - 2)
        // update navigationController viewControllers
        self.navigationController!.setViewControllers(navStackArray, animated:false)
    }
}

因此,您重写了 VC1 中的 performSegueWithIdentifier,以便在实际执行到 VC3 的转接(并且不仅在准备中)时更改导航堆栈。 这假定您要在 VC1 中处理这种特殊的导航逻辑。

我的解决方案是保留一个 BOOL 属性,说明何时应该跳到第三个,什么时候不跳过,比如在 VC2 中声明“shouldSkip”,这样如果你像下面那样在准备转场中设置它,你就可以按照VC2

中的做
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if let identifier = segue.identifier {
        switch identifier {
        case "JumpToThirdVCSegue":
            secondvc.shouldSkip=true

            }
        default: break
        }
    }
}

然后在 viewDidload VC2 中,你应该检查这个 BOOL,如果它是 true 并执行 segue,如果不是就继续 你也可以在不需要那种跳过的时候通过 pass

你可以使用方法

func setViewControllers(_ viewControllers: [UIViewController], animated: Bool)

这是解决 UIViewController 跳过问题的文档 (link)。 只需将所有需要的 UIViewControllers 传递给它(按导航顺序),最后一个将成为当前活动的 UIViewController(如果它已经不是活动的)。

if var navstack = navigationController?.viewControllers{
                    navstack.append(contentsOf: [vc1,vc2])
                    navigationController?.setViewControllers(navstack, animated: true)
                }

效果不错

根据 MarkHim 的回答,我在导航回插入的视图控制器时出现黑屏,因此我想出了以下解决方案。

顺便说一句 - 我想将新的视图控制器直接插入到我刚刚选择的视图控制器下,因此 navStack.count - 1 而不是 navStack - 2

这是我的解决方案:

override func performSegue(withIdentifier identifier: String, sender: Any?) {
    super.performSegue(withIdentifier: identifier, sender: sender)

    if identifier == "JumpToThirdViewControllerSegue",
    let navController = navigationController { // unwrap optional

        // initialize the view controller you want to insert
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewControllerToInsert = storyboard.instantiateViewController(
            withIdentifier: "SecondVC") as! SecondViewController

        // set any passed properties
        viewControllerToInsert.passedProperty = propertyToPass

        // create an object using the current navigation stack
        var navStackArray: [UIViewController]! = navController.viewControllers

        // insert the newly initialized view controller into the navStackArray
        navStackArray.insert(viewControllerToInsert, at: navStackArray.count - 1)

        // replace the current navigation stack with the one you just
        // inserted your view controller in to
        navController.setViewControllers(navStackArray, animated: false)
    }
}