25

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.

Panagiotis Korros
  • 10,840
  • 12
  • 41
  • 43

16 Answers16

59

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. :)

BastiBen
  • 19,679
  • 11
  • 56
  • 86
Aleks N.
  • 6,051
  • 3
  • 42
  • 42
  • Nicely done! Q: Is it possible for something else (internal to iPhone OS) to already be a delegate to the moreViewController? Not that you'd know for sure. I'm just wondering out loud here ... playing what if. – Joe D'Andrea Feb 01 '10 at 16:49
  • I was trying to do something similar (add an additional button on the leftBarButtonItem property of the 'More' Navigation Controller) and this provided the clues to the solution I was after - thank you. – andybee Dec 29 '10 at 20:13
  • 6
    I think it's lame that we should have to implement a hack like this when that Edit button really should automatically disappear if the customizableViewControllers array is empty or nil... but that's not Aleks' fault and his solution worked for me. – Kenny Wyland Jan 17 '11 at 00:34
  • 1
    Kenny Wyland, I agree 100% with that sentiment. – Joe D'Andrea Sep 24 '12 at 22:03
  • where to add this code? in AppDelegate or UITabBarController class? –  May 25 '16 at 07:19
  • does anyone now why it's only work for iPhone and not in iPad? – currarpickt Dec 02 '16 at 12:20
  • See my comment at [https://stackoverflow.com/a/42369292/236415] which is a method that does not accidentally remove the `rightBarButtonItem` for the VCs on More. – Jeff Apr 22 '19 at 06:58
  • @Jeff My answer is like 10 years old, could never think it would still be relevant back then! :-) – Aleks N. Apr 23 '19 at 11:45
55

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

tabBarController.customizableViewControllers = [NSArray arrayWithObjects:nil];
Ian Terrell
  • 10,667
  • 11
  • 45
  • 66
  • its removing that array..But how to hide that button..? – S.P. Mar 29 '10 at 07:17
  • 1
    It does indeed remove the edit button. Obviously the UITabBarController checks if there are any customisable buttons and only displays the edit button in that case. Alternatively, you could just set the property to nil instead of setting it to an empty array. – Johnus Sep 23 '10 at 03:06
  • Actually maybe not, it doesn't hide the button for me too. (ios 4.3) – Chris May 04 '11 at 02:26
  • For me, the tabBarController.customizableViewControllers = nil, solution works as long as I am creating the tabs programatically. If I ever revert back to the default tab bar settings (i.e. in the MainView.xib), then "edit" reappears. I solved it by always creating my tab-bar contents programatically. – paiego Jun 15 '11 at 23:01
  • Doesn't work for me - tried setting an empty array and `nil`. Using iOS 5.1. – BastiBen Apr 17 '12 at 16:32
  • Worked perfectly for me, thanks. Just dropped self.tabBarController.customizableViewControllers = [NSArray arrayWithObjects:nil]; into viewDidLoad and all is well with the world. – Robert Nov 01 '12 at 10:11
  • `tbc.customizableViewControllers = nil` works for me, but only if I put it after the `tbc.viewControllers = @[...]` line. Thanks for the simple answer. – dvs Jul 23 '14 at 15:27
  • Works for me on iOS7 (device) and iOS 8.1 and 9.0 (simulator), just dropped `self.customizableViewControllers = nil` in `viewDidLoad` of my custom UITabBarController. Tabs are created in storyboard. – Matěj Hrazdíra Nov 23 '15 at 15:46
  • Note that the contents of `customizableViewControllers` is reset any time `viewControllers` is touched. So you have to set it to `nil` (or `[]`) any time you set `viewController`. Also note, *you must set it*. Overriding `customizableViewControllers` to return `nil` doesn't hide the edit button. You have to go through the setter for the edit button to be hidden. – jemmons Jun 22 '18 at 14:26
  • This is the recommended way of removing the Edit button, as per the [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/1621184-customizableviewcontrollers). To quote, _If the array is empty or the value of this property is nil, the tab bar does not allow any items to be rearranged._ – Dobster May 26 '20 at 07:33
12
tabBarController .customizableViewControllers = nil;
DD_
  • 7,230
  • 11
  • 38
  • 59
m4rkk
  • 511
  • 2
  • 10
  • Thank you, this doesn't hide or disable the 'Edit' button, but it displays an empty customization dialog. It's better than nothing, but it doesn't do exactly what I want. – Panagiotis Korros May 05 '09 at 14:40
  • 1
    I did the following in my App Delegate didFinishLaunching method, after the initialization of the tabBarController. Worked great. self.tabBarController.customizableViewControllers = nil; – DenVog May 24 '13 at 18:35
6

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.

Jian Yu
  • 63
  • 1
  • 2
4

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
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Andre Oporto
  • 1,236
  • 9
  • 8
3

@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;
BenMorel
  • 34,448
  • 50
  • 182
  • 322
odyth
  • 4,324
  • 3
  • 37
  • 45
3

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;   
}
3

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;
}
Rob
  • 4,404
  • 2
  • 32
  • 33
  • Huh ... and now I'm upgraded to iOS4, and surprise, surprise - the Edit button has come back. – Rob Aug 21 '10 at 13:03
  • Huh ... and now I'm upgraded to iOS4, and surprise, surprise - the Edit button has come back. But eisornWolfs answer does work. I found that the easiest way to become a delegate was to put that code in my UITabbarController subclass: - (void)viewWillAppear:(BOOL)animated { self.moreNavigationController.delegate = self; } and then implement the method (- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated) right in the same class file. – Rob Aug 21 '10 at 13:21
3

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

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

}
Eshwar Chaitanya
  • 697
  • 10
  • 21
2

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;
}
Lee
  • 25
  • 7
1

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.

Edwin Otten
  • 122
  • 9
Bao Lei
  • 3,515
  • 3
  • 19
  • 17
1

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".

rubybeginner
  • 79
  • 1
  • 5
1

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.

Edwin Otten
  • 122
  • 9
1

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
}
Anthony F
  • 6,096
  • 4
  • 31
  • 32
0

The only solution that worked for me

self.moreNavigationController.navigationBar.topItem?.rightBarButtonItem?.title = ""
self.moreNavigationController.navigationBar.topItem?.rightBarButtonItem?.isEnabled = false
Alejandro Luengo
  • 1,256
  • 1
  • 17
  • 27
0

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
        }
    }
}
Chris
  • 1,750
  • 2
  • 14
  • 23