How to remove a UITabBar badge after the user clicks another tab? - iphone

I want to remove a badge as soon as the user clicks another tab. I am trying to do:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
UITabBarItem *tbi = (UITabBarItem *)self.tabController.selectedViewController.tabBarItem;
tbi.badgeValue = nil;
}
But it doesn't work.

You want to remove a badge from the current tab, or the one touched?
The right place to do this, either way, is in your tab bar controller delegate, in:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
Note that this function gets called whenever the user taps on a tab bar button, regardless of whether the new view controller shown is different from the old one, so you'll want to track your current visible view controller. This is where you'll update that, too:
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController {
if(viewController != self.currentTabVC) {
// if you want to remove the badge from the current tab
self.currentTabVC.tabBarItem.badgeValue = nil;
// or from the new tab
viewController.tabBarItem.badgeValue = nil;
// update our tab-tracking
self.currentTabVC = viewController;
}
}

Related

make UITabBar forget last state

When you have a UINavigationController integrated into one of the Tabs of a UITabBarController, the UITabBarController remembers where the user was last time the tab was used. Is it possible to have the UITabBarController forget and always start the tab as if it was the first time?
f.e.
structure is a follows
Tab1
View1
View2
Tab2
View1
The user starts at Tab1/View1, then he navigates to Tab1/View2. He changes to Tab2/View1 and then presses Tab1:
Current behavior: he appears at Tab1/View2
Wanted behaviour: he appears at Tab1/View1
Try like below it will help you
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
for(int i = 0; i < tabBarController.viewControllers.count; i++) {
if(tabBarController.selectedIndex != i && [[tabBarController.viewControllers objectAtIndex:i] isKindOfClass:[UINavigationController class]])
[[tabBarController.viewControllers objectAtIndex:i] popToRootViewControllerAnimated:NO];
}
}
Have a look at the UITabBar delegate protocole.
Then use tabBarController:didSelectViewController: that way :
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (viewController != tabBarItem1) {
[self.navigationControllerInTab1 popToRootViewControllerAnimated:NO];
}
}
That way, when you leave the first tab item, it will pop the NavigationController to the rootViewController.

TabBatController with viewControllers

In my tabBarController-based app, I have four tabs - tabA, tabB, tabC and tabD. The user will be able to jump between the tabs.
Now to my question:
- tabA, tabB and tabD have single viewControllers
- tabC though has 3 viewControllers - vc1, vc2 and vc3
I am running into issue where the app remembers which viewController the user was in last, and when the user taps tabC, the control goes to the last view controller that the user was in. For example, let's say the following is the sequence:
User taps tabA : view controller for tabA is shown
User taps tabD : view controller for tabD is shown
User taps tabC : view controller vc1 is shown. On tapping some action, the user is taken to vc3
User taps tabB : view controller for tabB is shown
User taps tabC : the vc3 is shown - instead I'd like to show vc1
So far, I have tried the following in vc1 of tabC, but the control does not come to vc1 at all:
- (void) viewWillAppear:(BOOL)animated {
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
}
How can I tell tabC to always load vc1?
Regards - thanks in advance....
Sam.
Your call to vc1 of tabC likely isn't getting called - viewWillAppear: only gets called when the view will appear on the display.
You might want to look at this function:
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
(Link to Apple Developer docs)
and this function:
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
(Link to Apple Developer docs)
If you implement this in your tabBarController delegate, you can act when the user selects tabC, ie:
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
if (viewController == \* tabC view controller */) {
[tabCViewController.navigationController popToRootViewController:YES];
}
}
#dvorak: Thanks for the answer - and sorry to drag on....
I know what I am writing is not an answer - but wanted to show the code that I am working with.
I am not having luck w/ the suggestion. I made the AppDelegate the TabBarControllerDelegate. The callback function gets called - however, using the following code, I am not able to popToRootViewController:
- (void)tabBarController:(UITabBarController *)tbController didSelectViewController:(UIViewController *)viewController {
NSLog(#"ViewController is <%#>", viewController.tabBarItem.title);
if ([viewController.tabBarItem.title isEqualToString:#"tabC"]) {
NSArray *tmp = [viewController.navigationController.tabBarController viewControllers];
[viewController.navigationController popToRootViewControllerAnimated:YES];
}
}
I collected the all ViewControllers in tmp variable , in a hope to see 3 ViewControllers in the array, after visiting all three VCs of tabC. From tabC->vc3, I hit the tabB, and then hit tabC for my exercise. The tmp array had zero elements in the debugger.

iphone app - how to detect which tab bar item was last pressed [duplicate]

i have a tab bar based application, with more than 5 tab bar items - so i get 4 of them directly showing in the view and the rest available by selecting the "More" tab. When a tab bar item is pressed, i want to detect which one was it.
So, in the
- (void)tabBarController:(UITabBarController *)tabBarCtrl didSelectViewController:(UIViewController *)viewController method, i use tabBarCtrl.selectedViewController.title to get the item's title.
This works for the tabs visible in the view -that is the 4 first and the "More" tab- but does not work for the rest of my tab bar items which are shown in the list after pressing the "More" tab.
I can see that the didSelectViewController method is not even called when selecting a tab from the "More" list.
How can i detect any of them when pressed?
Thank you in advance.
How to get title of UITabBarItem in the More section?
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
NSLog(#"controller class: %#", NSStringFromClass([viewController class]));
NSLog(#"controller title: %#", viewController.title);
if (viewController == tabBarController.moreNavigationController)
{
tabBarController.moreNavigationController.delegate = self;
}
}
You can access the index of selected item by using following code in your UIViewController. It will always return yout tab's index.
self.tabBarController.selectedIndex;
So if you have e.g. 6 items you can go to the "More..." tab, select your "5th" item and selectedIndex will be 4. If you go to the More tab and select 6th item, it'll return 5.
EDIT: If you want to check current position of some UITabBarItem you can do this:
Firstly, in your XIB file you should edit the tag property of each tab, so that 1st tab will have tag = 100, 2nd - 200, 3rd - 300, etc.
Then in ViewController add this code:
UIViewController *selectedVC = [self.tabBarController.viewControllers objectAtIndex:self.tabBarController.selectedIndex];
int selectedItemTag = selectedVC.tabItem.tag;
Then you can determine what viewController is it by using selectedItemTag variable. In this case, you can determine selectedIndex by doint this: selectedIndex = (selectedItemTag-100)/100.
The tag properties are not changed when customizing your UITabBar, so you can trust them :)
You can detect when a tab has been pressed using the UITabBarDelegate methods: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITabBarDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intf/UITabBarDelegate
You can make your UITabBarController class be the delegate and add the method in the implementation:
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
NSLog(#"tab selected: %#", item.title);
}
1. So if you are using a UITabBarController you can make the class implement the UITabBarControllerDelegate and set your UITabBarController delegate to the class that needs to be notified when the TabBar selected item changes, then add the delegate method to your class:
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
Inside this method you can use the UITabBarController selectedIndex property to know which is the current index selected:
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController: (UIViewController *)viewController
{
NSLog(#"Selected index: %d", tabBarController.selectedIndex);
}
2. If you are not using just the UITabBar you can follow the answer here by Ken Pespisa and iCoder in this post Ken Pespisa and iCoder in this post.
Since you add tags to your EVERY UITabBarItem (even those with index 5 and more).
You can track what tab was selected using following code:
//MARK: - UITabBarControllerDelegate
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if viewController == tabBarController.moreNavigationController {
tabBarController.moreNavigationController.delegate = self
} else {
setSelectedTabBarOption()
}
}
//MARK: - UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
setSelectedTabBarOption()
}
private func setSelectedTabBarOption() {
if let viewControllers = viewControllers {
let selectedController: UIViewController? = viewControllers.count > selectedIndex ? viewControllers[selectedIndex] : nil
if let tag = selectedController?.tabBarItem.tag {
//do whatever with your tag
}
}
}
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(#"Selected index: %d", tabBarController.selectedIndex);
if (viewController == tabBarController.moreNavigationController)
{
tabBarController.moreNavigationController.delegate = self;
}
NSUInteger selectedIndex = tabBarController.selectedIndex;
switch (selectedIndex) {
case 0:
NSLog(#"click tabitem %u",self.tabBarController.selectedIndex);
break;
case 1:
NSLog(#"click me again!! %u",self.tabBarController.selectedIndex);
break;
default:
break;
}
}
If you're using a tab bar controller, you should avoid knowing about the mapping between tab items and view controllers -- that's the job of the tab bar controller. If you're trying to use a tab bar for something else, then you should use UITabBar directly and not use UITabBarController. If you use UITabBar, you can set your own object as the tab bar's delegate, and the delegate will then get messages whenever the selected item changes.

Return to first view in UITabBarItem

When I receive a Push Notification, I want to jump to the middle tab on the tabbar and reset the tab (it has multiple table views that the user may have navigated into). I am able to jump to the tab using tabBarController.selectedIndex = 1; ... but how do I reset to the first view?
Implement UITabBarControllerDelegate on a controller class (perhaps your app delegate) and implement tabBarController:didSelectViewController: to pop all pushed view controllers off the navigation stack:
- (void) tabBarController:(UITabBarController *) tabBarController didSelectViewController:(UIViewController *) viewController {
if([viewController isKindOfClass:[UINavigationController class]])
{
[(UINavigationController *)viewController popToRootViewControllerAnimated:YES];
}
}

How to disable the edit button that appears in the more section of a UITabBarController?

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