Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practices for handling changes to the UINavigationItem of child view controllers in a container controller?

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:

  1. The selected view controller of the container controller changes and therefore the UINavigationItem of the container controller should update itself to mimic the UINavigationItem of the selected view controller.
  2. A child controller updates its UINavigationItem and the container controller must be made aware of the change and update its UINavigationItem to match.

The best solutions I've come up with are:

  1. In the setSelectedViewController: method query the navigation item of the selected view controller and update the leftBarButtonItems, rightBarButtonItems and title properties of the container controller's UINavigationItem to be the same as the selected view controller's UINavigationItem.
  2. In the setSelectedViewController method KVO onto the leftBarButtonItems, rightBarButtonItems and title property of the selected view controller's UINavigationItem and whenever one of those properties changes up the container controller's UINavigationItem.

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?

like image 924
Reid Main Avatar asked Aug 01 '13 19:08

Reid Main


People also ask

How many Uiviewcontrollers can a single UINavigationController hold?

The navigation stack isn't limited any more to 4 view controllers. Björn B. Björn B. Björn B.

What is child view controller in Swift?

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.


1 Answers

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]; } 
like image 181
Reid Main Avatar answered Oct 05 '22 08:10

Reid Main