Trying to understand TabBarDelegate - objective-c

In one of my ViewControllers I have the following code:
- (void)viewDidLoad
{
UITabBarController *tabBarController = (UITabBarController*)[UIApplication sharedApplication].keyWindow.rootViewController ;
[tabBarController setDelegate:self];
}
and :
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController: (UIViewController*)viewController {
NSLog(#"Yup!");
}
Whenever I switch tabs in my multi-tab setup, the console spits out
Yup
just as expected.
However, when I add
UITabBarController *tabController = (UITabBarController*)self.window.rootViewController;
tabController.selectedIndex = 1;
to my AppDelegate.m's
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:
the 'Yup' doesn't show anymore.
How come?

didSelectViewController will call when you select/change tabs from the app itself, it will not call when you set the selectedIndex programmatically
tabController.selectedIndex = 1; , will mostly useful when you want to set default tab or want to change the selectedIndex programmatically
From apple doc:
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
it is called only in response to user taps in the tab bar and is not
called when your code changes the tab bar contents programmatically.
You can try calling that method manually like this:
- (void) selectedItemWithIndex:(int)value {
tabbar.selectedIndex = value;
[self tabBarController:tabbar didSelectViewController:tabbar.viewControllers.firstObject];//place you vc here by array or manually
}
Ref: https://stackoverflow.com/a/30700712/4557505

Related

Reload ViewController by clicking on TabBarItem

I'm kinda desperate right now :/
I have a Tab Bar Controller with 4 Items. In the 4. Tab I included a webView which shows a list of pdf's. If I open a PDF in the webView there is no way to go back to the main webView with the links. Is there a way by re-clicking the 4. TabBar to reload the View? If I change from the 3. to the 4. tabbar it works (viewWillAppear).
Someone told me, that the following method should work:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
if ([viewController isKindOfClass:[UIColor class]]) {
//Try this if you're pushing the webView in another ViewController
[viewController.navigationController popToRootViewControllerAnimated:YES];
//or access to your webView and call goBack();
}
}
but actually I have no idea in which file I should insert that method. (See print Screen)
Thanks a LOT in advance for your help guys!
Subclass UITabBarController
1.1. Cmd+N and create a new instance of NSObject class, and name it TabBarController
1.2. in TabBarController.h replace NSObject so that it reads #interface TabBarController : UITabBarController <UITabBarControllerDelegate>
1.3. in TabBarController.m add this:
- (id) init
{
self = [super init];
if (self)
{
self.delegate = self;
}
return self;
}
1.4. and this
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
// Is this the view controller type you are interested in?
if ([viewController isKindOfClass:[MehrViewController class]])
{
// call appropriate method on the class, e.g. updateView or reloadView
[(MehrViewController *) viewController updateView];
}
}
1.5. In IB, Inspection, change the class of Tab Bar Controller to your TabBarController (instead of UITabBarController)
1.6. You also need to include MehrViewController.h in TabBarController.m
Edit
in MehrViewController.m (as you posted in your question, assuming it has a webView)
// An example of implementing reloadView
- (void)reloadView {
[self.webView reload];
}

Correct pattern for refreshing data in UIViewController's

I am trying to refresh my view data when the application becomes the active app again. I believe the correct pattern is to have the app delegate tell it's view to reload it's data when applicationDidBecomeActive is called.
However, I am having trouble finding the UIViewController from within the Delegate:
- (void)applicationDidBecomeActive:(UIApplication *)application {
MyFancyViewController* controller = //how do I get my view controller???
[controller refreshData];
}
Also, can I count on the view controller still being allocated, or is there a chance it would go away? I'm using iOS 5 Storyboard's if that makes any difference.
Update:
I think I got it:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Yeah, this does the trick:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
UIViewController* root = _window.rootViewController;
UINavigationController* navController = (UINavigationController*)root;
OctainViewController* mycontroller = (OctainViewController*)[[navController viewControllers] objectAtIndex:0];
[mycontroller refresh:nil];
}
Why not refresh your data in the respective view controller viewWillAppear method?

Objective C: How to disable user interaction to all of tab bars except one?

As what the title suggests, I would like to be able to lock all my tab bars except for one. And only after the user completes an action will I enable all the rest of the tab bars. How can I do that?
I haven't tried it, but according to the docs, you can return NO from the tabBarController:shouldSelectViewController: delegate.
[UPDATE] I just tried that out of curiosity - it seems to work fine. Create a new project from the "Tab bar application" template and then go to the -viewDidLoad of your FirstViewController. Add this line:
[self.tabBarController setDelegate:self];
and then implement the delegate method:
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
if (userHasCompletedAction) {
return YES;
}
return NO;
}
Don't forget to conform to <UITabBarControllerDelegate> in your .h file!
Hope that helps.
You have to implement this method
- (void)tabBarController:(UITabBarController *)tabBarController1 didSelectViewController:(UIViewController *)viewController {
if ([tabBarController1 selectedIndex]==0) {
UITabBarItem *tabBarItem = [[[[self tabBarController]tabBar]items] objectAtIndex:1];
[tabBarItem setEnabled:FALSE];
}
}
You have to do something like this for disabling your required tabbar items.
The method tabBar:didSelectItem: in UITabBarDelegate could help.

Objective C: How to reload a view controller's table view when tab is selected

I need to reload the data in a view controller when it's tabbar is clicked.
I am using the UITabBarControllerDelegate method as below:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if (tabBarController.selectedIndex == 3)
{
[(SomeViewController *)viewController getData];
}
}
where 'getData' is an instance method in SomeViewController class. However when I run my app, I get the following error
2011-07-01 02:12:11.193 onethingaday[19169:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController getData]: unrecognized selector sent to instance 0x600d500'
Can anyone advise me how I can overcome this issue? I just need to trigger the 'getData' method when tabbarcontroller.selected index ==3
It seems to me from the error message you get, that you use a UINavigationController in your tab controller; in this case, you cannot send directly the getData message to it; you should first find out which view controller under the UINavigationController should receive that message. (This is not actually related to the tab bar selectedIndex)
I don't know how your UINavigationController is organized, but you could do:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 3) {
//-- option 1: getData goes to the first view controller in the UINavigationController:
[[(SomeViewController*)[(UINavigationController*)viewController topViewController] getData];
//-- option 2: getData goes to the last view controller in the UINavigationController (the visible one):
[[(SomeViewController*)[(UINavigationController*)viewController visibleViewController] getData];
}
}
If you give more details about the organization of your UINavigationController I can help further identifying the right option.
Anyway, as you can see from the casts, there is something that is not fully ok with your design. I would strongly suggest using a notification for that. I.e., your SomeViewController registers itself for a notification of a given type :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someSelector:)
name:ShouldGetDataNotification
object:nil];
and the tab bar controller sends the notification for your controller to react upon:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
if (tabBarController.selectedIndex == 3) {
[[NSNotificationCenter defaultCenter] postNotificationName:ShouldGetDataNotification object:nil];
}
....
}
Look at this post.
See the solution to InterfaceBuilder - UIViewController subclass not recognized as subclass
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
if (tabBarController.selectedIndex == 3)
{
[[[(UINavigationController *)viewController viewControllers] objectAtIndex:2] getData];//2 for 3rd tabbar since 0,1,2
}
}
You could implement the -viewWillAppear method in your UITableViewController subclass. That should be called automatically when the UITabBarController switches to the view. It should look something like this:
- (void)viewWillAppear {
[super viewWillAppear];
[self getData];
}

iphone app - detect which tab bar item was pressed

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.