Question

In my application (based on the Tab bar application XCode template) I use a UITabBarController to display a list of different sections of the application that the user can access.

By default, the UITabBarController displays a 'More' button in the tab bar when there are more than 5 items. Also, it allows the user to select the items that he want to be visible in the tab bar.

Currently I can't implement saving and loading the state of the tab bar controller, so I want to disable the 'Edit' button.

Is there any way to disable/hide the 'Edit' bar button that appears on the 'More' navigation controller of UITabBarController?

I tried:

tabBarController.moreNavigationController.navigationBar.topItem.rightBarButtonItem = nil;

and

tabBarController.moreNavigationController.navigationBar.topItem.rightBarButtonItem.enabled = NO;

but they don't seem to work.

Was it helpful?

Solution

Become a delegate of moreNavigationController (it is a UINavigationController) and add this:

- (void)navigationController:(UINavigationController *)navigationController
        willShowViewController:(UIViewController *)viewController
        animated:(BOOL)animated {

    UINavigationBar *morenavbar = navigationController.navigationBar;
    UINavigationItem *morenavitem = morenavbar.topItem;
    /* We don't need Edit button in More screen. */
    morenavitem.rightBarButtonItem = nil;
}

Now it won't appear. The key thing to consider is that Edit button appears not after controller creation, but before displaying the view, and we should sit silently till that moment and then, when the controller is going to display the screen, we will knock the button out so that it won't have a chance to create it again. :)

OTHER TIPS

customizableViewControllers is an array; set it to the empty array to disable all editing.

tabBarController.customizableViewControllers = [NSArray arrayWithObjects:nil];
tabBarController .customizableViewControllers = nil;

i have tried and here's a example.

In AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

    // Override point for customization after application launch.

    // Add the tab bar controller's view to the window and display.
    [self.window addSubview:tabBarController.view];
    [self.window makeKeyAndVisible];

    //setting delegate to disable edit button in more.
    tabBarController.moreNavigationController.delegate = self;

    return YES;
}

to remove the "Edit" Button

    - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
        UINavigationBar *morenavbar = navigationController.navigationBar;
        UINavigationItem *morenavitem = morenavbar.topItem;
        /* We don't need Edit button in More screen. */
morenavitem.rightBarButtonItem = nil;
}

In your AppDelegate.h

@interface TestAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate, UINavigationControllerDelegate>

correct me if i'm wrong.

I was able to get this working with the following code. I created a CustomTabViewController and then modified my Tab Bar Controller's Class Identity in Interface Builder to use this custom class. Here is the code that it uses (.h and .m file contents). The key is setting the property to nil, which causes the Edit button to not be displayed. For details see: http://developer.apple.com/library/ios/documentation/uikit/reference/UITabBarController_Class/Reference/Reference.html#//apple_ref/occ/instp/UITabBarController/customizableViewControllers "If the array is empty or the value of this property is nil, the tab bar does not allow any items to be rearranged."

#import <UIKit/UIKit.h>

@interface CustomTabBarController : UITabBarController {

}
@end

#import "CustomTabBarController.h"


@implementation CustomTabBarController

- (void)viewDidLoad
{
    self.customizableViewControllers = nil;
    [super viewDidLoad];
}   

@end

This can be achieved like such. It is not the most elegant solution, but It Works™.

// Optional UITabBarControllerDelegate method
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
    [self performSelector:@selector(removeEdit) withObject:nil afterDelay:.0001];
}
- (void)removeEdit
{
    tabBarController.moreNavigationController.navigationBar.topItem.rightBarButtonItem = nil;   
}

Simply add a line of code in life cycle method i.e. application did finish launching:

- (void)applicationDidFinishLaunching:(UIApplication *)application
{ 
    tabBarController.customizableViewControllers=nil;

}

@m4rkk & @lan terrell that code does not work.

I wasn't able to get it so I just disable the navigation bar altogether.

tabBarController.moreNavigationController.navigationBar.hidden = YES;

I don't know about iOS4, but it matters if you put the code in viewDidLoad vs viewWillAppear.

Ie., this will work.

- (void)viewWillAppear:(BOOL)animated
{
self.customizableViewControllers = nil;
}

If you use NavigationController as your 1st ViewController and press one of the button to enter UITabBarController. Then apart from adding the code below,

- (void)navigationController:(UINavigationController *)navigationController
        willShowViewController:(UIViewController *)viewController
        animated:(BOOL)animated 
{
    UINavigationBar *morenavbar = navigationController.navigationBar;
    UINavigationItem *morenavitem = morenavbar.topItem;
    /* We don't need Edit button in More screen. */
    morenavitem.rightBarButtonItem = nil;
}

you need to add this "if statement" to avoid the edit button shows up when you first click the 5th ViewControllers and above.

if (self.selectedIndex >= 4) 
{
    self.customizableViewControllers = nil;
}

At the ones working with Xcode greater than 4.0 (I'm working on Xcode 4.2 for Snow Leopard):

Check at first where do you change the array of views the last time. I think it doesn't matter in which method you set your customizableView-Array to nil. Apples description says:

Important: Adding or removing view controllers in your tab bar interface also resets the array of customizable view controllers to the default value, allowing all view controllers to be customized again. Therefore, if you make modifications to the viewControllers property (either directly or by calling the setViewControllers:animated: method) and still want to limit the customizable view controllers, you must also update the array of objects in the customizableViewControllers property.

It worked for me, so please try it out. I found this description here: link to the description on developer.apple.com at chapter "Preventing the Customization of Tabs".

An iPhone 6 Plus will allow more buttons on the tab bar in landscape mode than in portrait. Unfortunately this means it resets the customizableViewControllers array whenever the device is rotated, and none of the answers here worked for me.

I already had my own UITabBarController subclass and overriding the setter and getter methods for customizableViewControllers was the only reliable way to remove the Edit button from the More screen:

- (NSArray *)customizableViewControllers
{
    return nil;
}

- (void)setCustomizableViewControllers:(NSArray*)controllers
{
    //do nothing
}

This is a late addition but I think it is a helpful contribution. Aleks N's answer can create a situation where the rightBarButtonItem is removed for every view controller under the "More Tab" (as Bao Lei mentioned). I would like to recommend using Bao Lei's Code, but with the difference of implenting it it the didShowViewController delegate method.

As his code exists now, users tapping the "More" tab to return to the base UIMoreViewController table can cause rightBarButtonItem's belonging to other viewControllers to be set to nil.

- (void)navigationController:(UINavigationController *)navigationController
       didShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    if (navigationController.viewControllers.count == 1)
    {
        UINavigationBar *morenavbar = navigationController.navigationBar;
        UINavigationItem *morenavitem = morenavbar.topItem;
        /* We don't need Edit button in More screen. */
        morenavitem.rightBarButtonItem = nil;
    }
}

The distinction is small but it took me a considerable amount of time to find this bug.

Aleks N's answer works, but I'd like to modify a little bit

- (void)navigationController:(UINavigationController *)navigationController
      willShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated
{
    if (navigationController.viewControllers.count == 1)
    {
        UINavigationBar *morenavbar = navigationController.navigationBar;
        UINavigationItem *morenavitem = morenavbar.topItem;
        /* We don't need Edit button in More screen. */
        morenavitem.rightBarButtonItem = nil;
    }
}

Since this delegate method is called every time when a view controller is pushed or popped on this view stack. When we are pushing other views onto this "More" view controller, we don't want to do this.

The only solution that worked for me

self.moreNavigationController.navigationBar.topItem?.rightBarButtonItem?.title = ""
self.moreNavigationController.navigationBar.topItem?.rightBarButtonItem?.isEnabled = false

I tried most of these solutions and was running into an issue where the edit button would return when rotating the device. The rotation would reset back to the first view controller, then when i returned to the more view controller, the edit button was there. The best solution was to become the UITabBarControllerDelegate and set the right bar button to nil anytime the more view controller became the selected view controller. This is working for iOS 11-12.

final class DashboardController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        delegate = self
    }
}

extension DashboardController: UITabBarControllerDelegate {
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        if viewController == moreNavigationController {
            moreNavigationController.navigationBar.topItem?.rightBarButtonItem = nil
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top