Adding same button to all view controllers in UINavigationController

I have a UINavigationController (to use like a wizard page) which I create programmatically and I need to display a “Cancel” button to cancel the process in any UIViewController.

Creating the UINavigationController:

  • Is there a way to change Views in iOS without using UINavigationController?
  • iPhone hide Navigation Bar only on first page
  • How can I make a UINavigationController's UINavigationBar invisible or at least change color
  • iOS Application launch black screen, UINavigationController, Nib, RootViewController
  • Setting tint color for embedded UISearchbar in Navigation bar
  • Hide navigation bar
  • FirstVC *firstVC = [[[FirstVC alloc] initWithNibName:@"FirstPage" bundle:nil] autorelease];
    firstVC.delegate = self;
    
    navigationController = [[UINavigationController alloc] initWithRootViewController:firstVC];
    [self.view addSubview:navigationController.view];
    

    Adding Cancel Button:

    UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelRequestNewLeave:)];
    navigationController.topViewController.navigationItem.rightBarButtonItem = cancelButton;
    [cancelButton release];
    

    But when I push a second page to UINavigationController the cancel button is not shown on the UINavigationBar. If I go back to first page, the cancel button is there. So, apparently the button is added only for the first view. I believe this is because I’m not subclassing UINavigationController, because I need to use it in a subview. But I don’t know how to set the rightBarButtonItem in a UINavigationController which is created programmatically.

    navigationController.topViewController.navigationItem.rightBarButtonItem = cancelButton;
    

    Can someone shed a light on this?

    Thanks in advance.

    8 Solutions Collect From Internet About “Adding same button to all view controllers in UINavigationController”

    The navigation item is per view controller. The navigation bar draws its contents from the navigation item of the view controller whose view it’s currently framing, which corresponds to the view controller at the top of the navigation controller’s stack.

    You basically need each view controller to stick a cancel button in its navigation item. You can do any of the following:

    • Copy-paste the code into all relevant view controllers.
    • Move the code into a utility function or class and call that.
    • Create a common superclass for all relevant view controllers that handles setting up the cancel button for its subclasses.

    You can also subclass UINavigationcontroller and overide few methods like this:

    - (id)initWithRootViewController:(UIViewController *)rootViewController {
        self = [super initWithRootViewController:rootViewController];
        if (self) {
            [self setCloseButtonToController:rootViewController];
        }
        return self;
    }
    
    - (void)dismissController {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)setCloseButtonToController:(UIViewController *)viewController {
        UIBarButtonItem *closeItem = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStylePlain target:self action:@selector(dismissController)];
        [viewController.navigationItem setRightBarButtonItem:closeItem];
    }
    
    - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
        [super pushViewController:viewController animated:animated];
    
        [self setCloseButtonToController:viewController];
    
    }
    

    You can instead adopt the UINavigationControllerDelegate protocol in the class which creates the UINavigationController instance. You can also create the cancelButton in advance and then implement navigationController:willShowViewController:animated: like this,

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
        viewController.navigationItem.rightBarButtonItem = cancelButton;
    }
    

    You will have to remember to create and hold the cancelButton and not release it. This will also mean cancelRequestNewLeave: will have to be a method in class that creates the UINavigationController instance which is what it is right now I guess.

    1. create CommonViewController
    2. create FirstViewController (extends from CommonViewController)
    3. create SecondeViewController (extends from CommonViewController)
    4. add function common functions in the CommonViewController

    like that

    CommonViewController.h

    @interface CommonViewController : UIViewController
    
    -(void) initializeCartBarButton;
    
    @end
    

    CommonViewController.m

    #import "CommonViewController.h"
    
    @interface CommonViewController ()
    
    @end
    
    @implementation CommonViewController
    
    -(void) initializeCartBarButton {
    
    
        UIBarButtonItem *cartBarButton = [[UIBarButtonItem alloc] init];
        cartBarButton.title = @"cart";
        [cartBarButton setTarget: self];
        [cartBarButton setAction: @selector(goToCart:)];
    
        self.navigationItem.rightBarButtonItem = cartBarButton;
        }
    
    - (IBAction) goToCart:(id)sender {
        NSLog(@"");
      }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
      }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
      }
    
    @end
    

    FirstViewController.h

    #import <UIKit/UIKit.h>
    #import "CommonViewController.h"
    
    @interface FirstViewController : CommonViewController
    
    @end
    

    FirstViewController.m

    #import "FirstViewController.h"
    
    @interface FirstViewController ()
    
    @end
    
    @implementation FirstViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self initializeCartBarButton];
    }
    @end
    

    SecondViewController.h

    #import <UIKit/UIKit.h>
    #import "CommonViewController.h"
    
    @interface SecondViewController : CommonViewController
    
    @end
    

    SecondViewController.m

    #import "SecondViewController.h"
    
    @interface SecondViewController ()
    
    @end
    
    @implementation SecondViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self initializeCartBarButton];
    }
    @end
    

    note: you can add the code of initializeCartBarButton in the viewDidLoad of CommonViewController and delete this fuction from CommonViewController and from child class’s

    This is how I did it with UINavigationController subclass that is capable of dismissing every viewController pushed into it.

    class CustomNavigationController: UINavigationController, UINavigationControllerDelegate{
    
        //TODO: Use when we have more right bar button types.
        var rightBarButtonType: RightBarButtonType = .Close
    
        enum RightBarButtonType{
            case Close
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.delegate = self
        }
    
        // MARK: Private Functions
        private func addRightBarButtonTo(viewController: UIViewController){
    
            let barButtonItem: UIBarButtonItem!
            switch self.rightBarButtonType {
            case .Close:
                barButtonItem = UIBarButtonItem(image: UIImage(named: "ic_close_white"), style: .Done, target: self, action: #selector(CustomNavigationController.dismiss(_:)))
    
            }
            viewController.navigationItem.rightBarButtonItem = barButtonItem
        }
    
        // MARK: UINavigationController Delegate
        func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
            self.addRightBarButtonTo(viewController)
        }
    
        @objc func dismiss(sender: AnyObject){
            self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
        }
    }
    

    You’ll need to add the button in every view controller. You cannot do it by setting one once or sharing one between view controllers (in a sensible fashion). A good place to add the button is in the viewDidLoad method of your view controllers. You can create one basic UIViewConteoller subclass for them if you feel this gets to repetitive.

    You can add a custom ‘Cancel’ UIButton directly to the NavigationBar’s view instead of using the UIBarButtonItem.

    UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
    cancelButton.imageView = // Some custom image
    cancelButton.frame = CGRectMake(...);  // Something far to the right.
    [self.navigationController.navigationBar addSubview: cancelButton];
    

    The normal way to do this is to add that cancel button to the navigationItem of every single view controller in your navigation stack. The above approach can make it simpler by allowing you to write less code, but it is a tiny bit of a hack.

    Add this code in your rootview viewDidLoad method and implement the cancelMethod in rootview controller.This will be available in all the view controllers. you can adjust the button location by changing button frame.For orientation change you have manually adjust the location of button.

     UIButton *btnCancel = [UIButton buttonWithType:UIButtonTypeRoundedRect];
            [btnCancel addTarget:self
                            action:@selector(cancelMethod)
                  forControlEvents:UIControlEventTouchDown];
            [btnCancel setBackgroundImage:[UIImage imageNamed:@"image"]  
            forState:UIControlStateNormal];
            btnCancel.frame = CGRectMake(280, 27, 45, 25);
    
        [self.navigationController.view addSubview: btnCancel];