In my TabBarViewController, I'm creating a navigation and presenting it modally.
func viewDidLoad(){
super.viewDidLoad();
//Create a present this view controller in viewDidLoad
self.navController = UINavigationController()
self.presentViewController(self.navController, animated: true, completion: nil)
}
//This method gets called when TabBarVC gets a NSNotification
func showMessageForUser(user_id: Int){
let mvc = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
mvc.user_id = user_id //set this user
//display this to the user.
self.navController.pushViewController(mvc, animated: true)
}
This is pretty straight forward. However, I would like to do this:
All the "back" buttons should work as expected after reordering.
You can use setViewControllers(_:animated:)
to rearrange the view controller stack. You don't have to do anything special to make the back button work correctly. The navigation controller sets up the back button based on the second item in its viewControllers
array (if there is a second item), and updates the back button whenever it updates the viewControllers
array.
Here's how I'd do this. First, we add a method to UIViewController
to ask whether it's the view controller for a specific userId
. Since most view controllers are not (and cannot be) the correct view controller, it just returns false
:
extension UIViewController {
func isViewControllerForUserId(userId: Int) -> Bool {
return false
}
}
Then we override this method in MessagesViewController
to return true
when appropriate:
extension MessagesViewController {
override func isViewControllerForUserId(userId: Int) -> Bool {
return self.userId == userId
}
}
Now, to show the view controller for a specific user, we search the navigation controller's stack for an existing view controller. The action we take depends on whether we find it:
func showMessageForUserId(userId: Int) {
if let index = navController.viewControllers.indexOf({ $0.isViewControllerForUserId(userId) }) {
navController.moveToTopOfNavigationStack(viewControllerAtIndex: index)
} else {
pushNewViewControllerForUserId(userId)
}
}
If we didn't find it, we make a new view controller and push 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 did find it, we move it to the top of the navigation stack with this method:
extension UINavigationController {
func moveToTopOfNavigationStack(viewControllerAtIndex index: Int) {
var stack = viewControllers
if index == stack.count - 1 {
// nothing to do because it's 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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With