You can use setViewControllers(_:animated:) to redistribute the view controller stack. You do not need to do anything special for the back button to work correctly. The navigation controller sets the back button based on the second element in the viewControllers array (if there is a second element) and updates the back button whenever it updates the viewControllers array.
This is how I do it. First, add a UIViewController method to ask if it is a view controller for a specific userId . Since most view controllers are not (and cannot be) the correct view controller, it simply returns false :
extension UIViewController { func isViewControllerForUserId(userId: Int) -> Bool { return false } }
Then we will override this method in MessagesViewController to return true , if necessary:
extension MessagesViewController { override func isViewControllerForUserId(userId: Int) -> Bool { return self.userId == userId } }
Now, to show the view controller for a specific user, we are looking for the navigation controller stack for an existing view controller. The action we take depends on whether we find this:
func showMessageForUserId(userId: Int) { if let index = navController.viewControllers.indexOf({ $0.isViewControllerForUserId(userId) }) { navController.moveToTopOfNavigationStack(viewControllerAtIndex: index) } else { pushNewViewControllerForUserId(userId) } }
If we did not find it, we will create a new view controller and click it:
private func pushNewViewControllerForUserId(userId: Int) { let vc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController vc.userId = userId self.navController.pushViewController(vc, animated: true) }
If we find it, we will move it to the top of the navigation stack using this method:
extension UINavigationController { func moveToTopOfNavigationStack(viewControllerAtIndex index: Int) { var stack = viewControllers if index == stack.count - 1 { // nothing to do because it already on top return } let vc = stack.removeAtIndex(index) if (reorderingIsBuggy) { setViewControllers(stack, animated: false) } stack.append(vc) setViewControllers(stack, animated: true) } private var reorderingIsBuggy: Bool { // As of iOS 9.3 beta 3, `UINavigationController` drops the prior top-of-stack // when you use `setViewControllers(_:animated:)` to move a lower item to the // top with animation. The workaround is to remove the lower item from the stack // without animation, then add it to the top of the stack with animation. This // makes it display a push animation instead of a pop animation and avoids // dropping the prior top-of-stack. return true } }