Suppose I have a container controller that accepts an array of UIViewControllers and lays them out so the user can swipe left and right to transition between them. This container controller is wrapped inside a navigation controller and is made the root view controller of the application's main window.
Each child controller makes a request to the API and loads a list of items that are displayed in a table view. Based on the items that are displayed a button may be added to the navigation bar that allows the user to act on all the items in the table view.
Because UINavigationController only uses the UINavigationItems of its child view controllers, the container controller needs to update its UINavigationItem to be in sync with the UINavigationItem of its children.
There appear to be two scenarios that the container controller needs to handle:
The best solutions I've come up with are:
This is a recurring problem with many of the container controllers that I have written and I can't seem to find any documented solutions to these problems.
What are some solutions people have found to this problem?
The navigation stack isn't limited any more to 4 view controllers. Björn B. Björn B. Björn B.
Child view controllers are especially useful for UI functionality that we wish to reuse across a project. For example, we might want to display a loading view as we're loading the content for each screen — and that can easily be implemented using a child view controller, that can then simply be added when needed.
So the solution that I have currently implemented is to create a category on UIViewController with methods that allow you to set the right bar buttons of that controller's navigation item and then that controller posts a notification letting anyone who cares know that the right bar button items have been changed.
In my container controller I listen for this notification from the currently selected view controller and update the container controller's navigation item accordingly.
In my scenario the container controller overrides the method in the category so that it can keep a local copy of the right bar button items that have been assigned to it and if any notifications are raised it concatenates its right bar button items with its child's and then sends up a notification just incase it is also inside a container controller.
Here is the code that I am using.
UIViewController+ContainerNavigationItem.h
#import <UIKit/UIKit.h> extern NSString *const UIViewControllerRightBarButtonItemsChangedNotification; @interface UIViewController (ContainerNavigationItem) - (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems; - (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem; @end
UIViewController+ContainerNavigationItem.m
#import "UIViewController+ContainerNavigationItem.h" NSString *const UIViewControllerRightBarButtonItemsChangedNotification = @"UIViewControllerRightBarButtonItemsChangedNotification"; @implementation UIViewController (ContainerNavigationItem) - (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems { [[self navigationItem] setRightBarButtonItems:rightBarButtonItems]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter postNotificationName:UIViewControllerRightBarButtonItemsChangedNotification object:self]; } - (void)setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem { if(rightBarButtonItem != nil) [self setRightBarButtonItems:@[ rightBarButtonItem ]]; else [self setRightBarButtonItems:nil]; } @end
ContainerController.m
- (void)setRightBarButtonItems:(NSArray *)rightBarButtonItems { _rightBarButtonItems = rightBarButtonItems; [super setRightBarButtonItems:_rightBarButtonItems]; } - (void)setSelectedViewController:(UIViewController *)selectedViewController { if(_selectedViewController != selectedViewController) { if(_selectedViewController != nil) { // Stop listening for right bar button item changed notification on the view controller. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self name:UIViewControllerRightBarButtonItemsChangedNotification object:_selectedViewController]; } _selectedViewController = selectedViewController; if(_selectedViewController != nil) { // Listen for right bar button item changed notification on the view controller. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(_childRightBarButtonItemsChanged) name:UIViewControllerRightBarButtonItemsChangedNotification object:_selectedViewController]; } } } - (void)_childRightBarButtonItemsChanged { NSArray *childRightBarButtonItems = [[_selectedViewController navigationItem] rightBarButtonItems]; NSMutableArray *rightBarButtonItems = [NSMutableArray arrayWithArray:_rightBarButtonItems]; [rightBarButtonItems addObjectsFromArray:childRightBarButtonItems]; [super setRightBarButtonItems:rightBarButtonItems]; }
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