简体   繁体   中英

Skip/Add a View Controller in Navigation Stack

I have three View Controllers embedded in Navigation Controller. I want to go from VC1 to VC3 so that in VC3 the Navigation Item's back button would direct the user to VC2 instead of VC1. I think this should be done either by adding the VC2 to the Navigation Stack between VC1 and VC3 when VC3 is created or by skipping the second View Controller .

I tried this:

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
        }
    }
}

but I couldn't get it working. Perhaps performing a segue isn't possible when the View Controller isn't open yet?

What is the best way to skip a View Controller / add a View Controller in the middle of Navigation Stack? I hope this helps you to understand what I'm trying to do:

Something like this should work:

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

EDIT:

If you want to push the second view controller without being noticed by the user you need to add it to the navigation controller after the third view controller is pushed. This can be done by implementing UINavigationControllerDelegate . You can store your second view controller in a variable and insert it to the navigation controllers hierarchy in delegate method. Your main view controller will look like following:

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)
        }
    }

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

Works well

Edit: using swift and segues, this should work:

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)
    }
}

So you override the performSegueWithIdentifier in the VC1 in order to change the navigation stack when the segue to VC3 has actually been performed (and is not only in preparation). This assumes, that you want to handle this special navigation logic in VC1.

You can use method

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

Here's the documentation ( link ) that solves the UIViewController skipping problem. Simply pass all the needed UIViewControllers to it (in navigational order) and the last one will be made as the current active UIViewController (if it already isn't the active one).

My solution would be to keep a BOOL property of when you should skip to third and when not skip, like declaring 'shouldSkip' in VC2 so that if u set it in prepare segue like below you can act according to that in VC2

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

            }
        default: break
        }
    }
}

and then in viewDidload VC2 you should check this BOOL and if it is true and perform segue and if not just move on you could also pass pass whenever that kind of skip is not necessary

Building off of MarkHim 's answer, I was getting a Black screen when navigating back to the inserted view controller so I came up with the following solution.

BTW - I wanted to insert the new view controller directly under the view controller I just segued to hence the navStack.count - 1 instead of navStack - 2 .

Here is my solution:

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)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM