I have a tab bar with 3 items. Each points to a UINavigationController. Each UINavigationController has several viewControllers beneath. I'm wanting to reset back to the first controller in the navigation when any tab bar item is pressed.
I've specified my TabBarController implementation as a delegate
self.delegate = self and my method below (running in my TabBarController implementation works returning UINavigationControllers.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"%#", viewController);
}
With the log file showing e.g.
UINavigationController: 0x8a31a90>2012-12-31 02:16:40.035 Demo[6142:c07]
when I try popToRootViewController or popViewController within this method it doesn't seem to work. I don't get any errors but my viewControllers don't reset. It seems like I've made a really basic error here but I can't tell what.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"%#", viewController);
[self.navigationController popToRootViewControllerAnimated:YES];
[[self navigationController] popViewControllerAnimated:YES];
}
You need to popToRootViewController on the navigationController (viewcontroller) - not the TabViewController (self).
[viewController popToRootViewControllerAnimated:YES];
I'm not sure if this will help. I had a lot of trouble getting this to work and found that I needed to do the following:
In root view controller (first view app comes to), add a delegate in the .h file.
#interface MGMProductsViewController : UIViewController <UITabBarControllerDelegate>
Add the below code to viewDidLoad in root view controller (.m file).
[self.tabBarController setDelegate:self];
Override method in root view controller (.m file) with the below.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if ([viewController isKindOfClass:[UINavigationController class]])
{
[(UINavigationController*)viewController popToRootViewControllerAnimated:NO];
}
}
I can't attribute this to anyone as I couldn't find the appropriate code again. I think I pieced it together from a few places though the '[self.tabBarController setDelegate:self]' looked to be the key to it working for me.
Good luck.
I am assigning my view controller to my tab right after I create it. Is it possible to select the view that will show after the tab is clicked?
For eg
//user clicks tab 1
if(hasMessages)
//show view A
else
//show view B
Yes, it is possible. You need to set a delegate for your tab controller:
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
tabBarController.delegate = self; // or whatever suitable class you have
This delegate needs to conform to the UITabBarControllerDelegate protocol.
In your delegate, implement tabBarController:didSelectViewController: and inside it, find out which view you want to present. Assuming your tab's root view controller is a navigation controller, then the delegate method implementation would be something like this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
/* logic goes here */
[viewController pushViewController:someNewVC animated:YES];
}
i have a navigation application with a tab bar and there is a web view in first view which is related with first tab bar item . with a button web view appears and i want to hide it when i touch the first tab bar item .. Please help thx
To get the controller, just:
[[tabBarController viewControllers] objectAtIndex:indexOfTheTab]
tabBarController refers to UITabBarController *tabBarController in you AppDelegate.
EDIT 1: add this to your AppDelegate.h
-(UITabBarController*)getTabBarController;
add this to your AppDelegate.m
-(UITabBarController*)getTabBarController { return tabBarController; }
Now, you can access it from everywhere:
[(AppDelegate*)[[UIApplication sharedApplication] delegate] getTabBarController]
Don't forget #import "AppDelegate.h"
EDIT 2: In you AppDelegate.m
In the first method, just add: self.tabBarController.delegate = self;
Then, override:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
//something like : [tabBarController hideTheStuffs];
}
With EDIT 2 you can listen to the user touches in the tab bar, even if the tab item is already the current one. With EDIT 1 you can access your delegate from everywhere if needed.
EDIT 3:
Do you have a IBOutlet UIWebView *myWebView in you ControllerView.h (the one with the view that shows the web view).
If not, add this outlet, then connect it to your webview in interface builder.
In the controller, add a method in .h and .m :
-(void)hideTheWebView { myWebView.hidden = YES; }
As this method is declared in the interface (.h), you can call it from the AppDelegate, method tabBarController:didSelectViewController.
I have a tabBarController that I add by placing the following code into:
AppDelegate.h:
...
UITabBarController IBOutlet *tabBarController;
}
#property (nonatomic, retain) IBOutlet UITabBarController *tabBarController;
AppDelegate.m:
...
[self.window addSubview:tabBarController.view];
[self.window makeKeyAndVisible];
[tabBarController setDelegate:self];
I then use the following code to present a modal barcode scanning View Controller:
- (void)tabBarController:(UITabBarController *)tbc didSelectViewController:(UIViewController *)vc {
// Middle tab bar item in question.
if (vc == [tabBarController.viewControllers objectAtIndex:2]) {
ScanVC *scanView = [[ScanVC alloc] initWithNibName:#"ScanViewController" bundle:nil];
// set properties of scanView's ivars, etc
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:scanView];
[tabBarController presentModalViewController:navigationController animated:YES];
[navigationController release];
[scanView release];
}
}
When it does actually get presented I think this method isn't visually appealing, because when I dismiss the modal view I am brought back to an empty view.
A lot of barcode scanning applications or applications that simply display an image picker for example; do this quite successfully. I'm just wondering what kind of implementation they would use in order to achieve such an effect.
This is a screenshot of an application called Path, which has the exact same functionality I'm after:
I also noticed that in these applications, if you are on any other tab bar item other than the middle one let's say, and you click on the tab bar item that presents the modal view, once it gets dismissed it doesn't actually bring them back to an empty view it dismisses like normal, however the actual tab bar item that presents the modal view is never selected. I would be happy with this type of functionality if that's the only way to implement this type of effect.
Any help would be greatly appreciated as I've been stuck in this for quite some time. Also I'm not even sure whether it's the right way to put all of this code in my AppDelegate in order for the View Controller to be presented as a modal. It all seems, just, wrong.
Not entirely what I'm after, but I think I can move forward from this:
http://idevrecipes.com/2010/12/16/raised-center-tab-bar-button/
When you dismiss the modal view controller, tell the tab bar to select whatever tab was originally selected.
- (void)dismissModalViewControllerAnimated:(BOOL)animated
{
// do whatever you need to do when dismissing
// savedTabIndex is an int ivar
// tabBarController is a reference, set when showing the modal view
[[self tabBarController] setSelectedIndex:savedTabIndex];
}
You would have to save the original tab bar selection in a variable at the start of tabBarController:didSelectViewController:.
- (void)tabBarController:(UITabBarController *)tbc
didSelectViewController:(UIViewController *)vc
{
// Save the tab bar index (if it's not the photo tab)
if ([tabBarController selectedIndex] != 3]) {
savedTabIndex = [tabBarController selectedIndex];
}
}
There could be mistakes in this code, I just typed it without testing.
I found a really easy solution by playing around UITabBarControllerDelegate--I only tried this in iOS 7 though.
First, subclass UITabBarController, make it its own UITabBarControllerDelegate, and create a property that'll hold a reference to the tab you want to launch a modal with. In my app, it's called the "Sell" tab.
#property (strong, nonatomic) UIViewController *sellTab;
Then, in your init method, just create that view controller and add it to the tabs.
_sellTab = [[UIViewController alloc] init];
_sellTab.title = #"Sell";
self.viewControllers = #[homeTab, historyTab, _sellTab, bookmarksTab, profileTab];
Now here's where the magic is: override the following tab bar controller delegate methods. Code is pretty self-explanatory.
#pragma mark - Tab bar controller delegate
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
return viewController != self.sellTab;
}
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
if (item == self.sellTab.tabBarItem) {
[self presentViewController:[[UINavigationController alloc] initWithRootViewController:[[PostAdViewController alloc] init]] animated:YES completion:nil];
}
}
This will launch a modal which, upon dismissal, shows the same tab you were in before launch.
You shouldn't present a modal view, when the user clicks on a tab bar item.
You could instead present a modal view from within a view that's presented by one of the tabs.
Or, if you just have a single main view and the scan view you want to present modally, you should just use a button to present the scan view from within your main view. You could for instance use a toolbar with a single button in it, instead.
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.
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. :)
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
}
}
}