Passing data from view controller to tab bar controller in iOS
Asked Answered
M

6

9

I'm developing an iOS app and now I'm at a loss. I'm trying to pass data from the first View Controller to the first tab of a TabBarViewController (with using the storyboard). I found a lot of tutorials that explain how to pass data between view controllers, but nothing worked with my tab bar. I know that the tab bar controller contains a kind of array of views. The relation between the View Controller and the Tab Bar Controller is realized using a segue (push). So, I thought it is easy to use the prepareForSegue() - method. Like that:

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
 if ([[segue identifier] isEqualToString:@"tab"]) {

 // ...

    } 
 }  

Unfortunately, the relation between the Tab Bar Controller and each tab bar view is no real segue. It's only a "relationship". That means, there is no segue identifier I am able to use for the the method above-mentioned. Is there a possibility to use the prepareForSegue in this case? If not, any ideas how to solve this problem? I know that there is a similar question, but the answer wasn't that helpful. Do I have to create a new file for every tab (view) within the tab bar controller? Or is it possible to have one class (m. & h.) for the whole tab bar controller, accessing the several view with objectAtIndex()?

Thanks in advance!

Marcela answered 19/7, 2013 at 9:51 Comment(0)
S
8

Here's my setup that worked:

  1. Setup Segue:
    1. Setup View Controller with segue to Tab Bar Controller with 2 child View Controllers in Storyboard
    2. Specify segue identifier (tab)
  2. Setup Classes in Storyboard:
    1. View Controller class = ViewController
    2. Tab Bar Controller class = TabBarController
    3. Tab Bar Controller Child View Controller class = TabsViewController (shared between both)
  3. Setup labelString property in Tab Bar Controller:

    1. In TabBarController.h:

      @property (nonatomic, strong) NSString *labelString;
      
    2. In TabBarController.m:

      @synthesize labelString;
      
  4. Setup prepareForSegue method in ViewController.m:

    #import "TabBarController.h"
    
    ...
    
    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if ([[segue identifier] isEqualToString:@"tab"]){
            TabBarController *tabBar = [segue destinationViewController];
            [tabBar setLabelString:[NSString stringWithFormat:@"This has been set"]];
        }
    }
    
  5. Setup UILabels for Child Tab Bar View Controllers.

    1. Drag default UILabel controls into both child View Controllers
    2. Create property in TabsViewController.h:

      @property (nonatomic, strong) IBOutlet UILabel *label;
      
    3. Hook 5.1 and 5.2 up in Storyboard

  6. Setup ViewDidLoad method in TabsViewController.m:

    #import "TabBarController.h"
    
    ...
    
    @synthesize label;
    
    ...
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        TabBarController *tabBar = (TabBarController *)self.tabBarController;
        label.text = [NSString stringWithFormat:@"Tab %i: %@",[tabBar.viewControllers indexOfObject:self],tabBar.labelString];
    }
    

Now clicking on the 1st and 2nd tabs will have the labels display Tab 0: This has been set and Tab 1: This has been set, respectively.

Sevigny answered 19/7, 2013 at 12:53 Comment(7)
It seems to be logic. I tried to realize that, but I get an exception. At this moment, I don't know why ..Marcela
I was confusing myself with all the tab bar class names in my answer so I may have switched around some of them in my original post. Just recently edited to display more logically. Confirmed to work on my end.Sevigny
I think there is a problem with 'setLabelString', because: 2013-07-19 15:20:54.412 iCRS Master[1364:11303] -[UITabBarController setLabelString:]: unrecognized selector sent to instance 0x7550810 2013-07-19 15:20:54.414 iCRS Master[1364:11303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITabBarController setLabelString:]: unrecognized selector sent to instance 0x7550810'.Marcela
Weirdly enough I got this too while building it up from scratch. Have you tried cleaning the build folder (Option-Shift-Command-K) then re-running?Sevigny
@Kalpesh Yes I tried that and it's also possible. You'd just move the property into TabsViewController, and in prepareForSegue you would just have to loop through all of the Tab Bar's Child View Controller and set each 1, it's just that @Marcela had a TabBarController already setup.Sevigny
@Sevigny & Kalpesh: Thank you so much. It works! The reason for the exception in the end was that I forgot to setup the classes in storyboard. ;-) stupid mistake. But your code examples really helped me!Marcela
@Marcela No problem :) Glad to have helped. Please accept the answer or at least an upvote if you've already accepted another. Thanks!Sevigny
A
8

If your hierarchy is Viewcontroller->UItabbarcontroller->ViewCOntroller

in my case i have to send data to marketviewcontroller . In tabcontroller, marketviewcontroller is present at index 0.

  - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  {
 if ([[segue identifier] isEqualToString:@"tab"])
  {
    UITabBarController *tabar=segue.destinationViewController;
    MarketViewController *marketViewcontroller=[tabar.viewControllers objectAtIndex:0];

   // pass data to market view controller
    [marketViewcontroller passobject:Yourdata];
 //   or 
  marketViewcontroller.value=Yourdata
  } 
  }  

in MarketViewController.h 
 @property(nonatomic,retain) NSString * value;
Agustin answered 19/7, 2013 at 9:57 Comment(3)
Yes, the hierarchy is like that. Are you using one class for the whole Tab Bar Controller? Or is 'MarketViewController' the first view of your tab bar?Marcela
Okay, I created it like that I think. But what's with this 'passobject'? I got the following error: No visible @interface for 'TabOneViewController' declares the selector 'passpbject'. Do you know my mistake?Marcela
Sorry, there were so many answers now. I'm really surprised. I tried to implement the version of Yazid and now it works. But your idea is similar I think. Thank you very much for your fast reaction!Marcela
S
8

Here's my setup that worked:

  1. Setup Segue:
    1. Setup View Controller with segue to Tab Bar Controller with 2 child View Controllers in Storyboard
    2. Specify segue identifier (tab)
  2. Setup Classes in Storyboard:
    1. View Controller class = ViewController
    2. Tab Bar Controller class = TabBarController
    3. Tab Bar Controller Child View Controller class = TabsViewController (shared between both)
  3. Setup labelString property in Tab Bar Controller:

    1. In TabBarController.h:

      @property (nonatomic, strong) NSString *labelString;
      
    2. In TabBarController.m:

      @synthesize labelString;
      
  4. Setup prepareForSegue method in ViewController.m:

    #import "TabBarController.h"
    
    ...
    
    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if ([[segue identifier] isEqualToString:@"tab"]){
            TabBarController *tabBar = [segue destinationViewController];
            [tabBar setLabelString:[NSString stringWithFormat:@"This has been set"]];
        }
    }
    
  5. Setup UILabels for Child Tab Bar View Controllers.

    1. Drag default UILabel controls into both child View Controllers
    2. Create property in TabsViewController.h:

      @property (nonatomic, strong) IBOutlet UILabel *label;
      
    3. Hook 5.1 and 5.2 up in Storyboard

  6. Setup ViewDidLoad method in TabsViewController.m:

    #import "TabBarController.h"
    
    ...
    
    @synthesize label;
    
    ...
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        TabBarController *tabBar = (TabBarController *)self.tabBarController;
        label.text = [NSString stringWithFormat:@"Tab %i: %@",[tabBar.viewControllers indexOfObject:self],tabBar.labelString];
    }
    

Now clicking on the 1st and 2nd tabs will have the labels display Tab 0: This has been set and Tab 1: This has been set, respectively.

Sevigny answered 19/7, 2013 at 12:53 Comment(7)
It seems to be logic. I tried to realize that, but I get an exception. At this moment, I don't know why ..Marcela
I was confusing myself with all the tab bar class names in my answer so I may have switched around some of them in my original post. Just recently edited to display more logically. Confirmed to work on my end.Sevigny
I think there is a problem with 'setLabelString', because: 2013-07-19 15:20:54.412 iCRS Master[1364:11303] -[UITabBarController setLabelString:]: unrecognized selector sent to instance 0x7550810 2013-07-19 15:20:54.414 iCRS Master[1364:11303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITabBarController setLabelString:]: unrecognized selector sent to instance 0x7550810'.Marcela
Weirdly enough I got this too while building it up from scratch. Have you tried cleaning the build folder (Option-Shift-Command-K) then re-running?Sevigny
@Kalpesh Yes I tried that and it's also possible. You'd just move the property into TabsViewController, and in prepareForSegue you would just have to loop through all of the Tab Bar's Child View Controller and set each 1, it's just that @Marcela had a TabBarController already setup.Sevigny
@Sevigny & Kalpesh: Thank you so much. It works! The reason for the exception in the end was that I forgot to setup the classes in storyboard. ;-) stupid mistake. But your code examples really helped me!Marcela
@Marcela No problem :) Glad to have helped. Please accept the answer or at least an upvote if you've already accepted another. Thanks!Sevigny
H
1

How are you showing your TabBarViewController from your UIViewController?

I am guessing with a segue. If this is the way you're doing it, you can pass data to this UITabBarController which is the "parent" for all the controllers inside the tabs.

Say you want to pass a string to the UITabBarController, you would define a property in this controller and set it before the segue.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
  {
      if ([[segue identifier] isEqualToString:@"tab"])
      {
          UITabBarController *tabBarVC=segue.destinationViewController;
          tabBarVC.stringToSet = @"hi";
      }
  }

Then, with the delegate method, you have the selected view controller, so you can pass the parent property to the children:

- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController {
    viewController.stringToSet = self.stringToSet // self is UITabBarController
}
Hanni answered 19/7, 2013 at 10:29 Comment(3)
Thanks for your idea. Yes, I'm using a segue. I have a button within the first View Controller which is connected to the Tab Bar Controller. And the delegate method above is used to 'send' the string to every child?Marcela
Okay, I use prepareForSegue within the ViewController. Then, I create a property within the Tab Bar Controller, right? Where exactly do you call the delegate method? And can you please tell me how I am able to access the passed data use it within the tab bar?Marcela
Sorry to answer this late, maybe you already fixed this. Delegate is a very common pattern in cocoa development. You set the delegate of your UITabBarController and then its delegate methods should be automatically called. More information here: developer.apple.com/library/ios/#documentation/general/…Hanni
J
1

Have a look at singletons. Inside a tab you create an instance of a helper class which implements the singleton template, ensuring that there is actually only one single instance. Which means when the second tab instantiates the same helper class it will have access to the same object, which you can use to share your data ...

e.g.: http://www.galloway.me.uk/tutorials/singleton-classes/

Juanjuana answered 19/7, 2013 at 10:46 Comment(3)
The problem was to pass the data from another view controller to the tab of the tab bar controller. I think your answer is helpful if one tries to pass data between different tabs of the tab bar, isn't it?Marcela
actually, you can use singletons to pass data between anything, it does not even have to be a view controller ...Juanjuana
Really good point, @Martin Mandl. You inspired me, using singletons is much better for "scalable scenario". Thank you!Hanus
W
0

First thing:

1- You want to pass the data to your tab for some reason...

or:

2- You want to pass the data to your tab so then you can pass to another UIViewController?

If it's the second option, the most common pattern for that is the Singleton. Where you can keep common data, that can be used/reused by different parts of the application.

Warmup answered 19/7, 2013 at 9:53 Comment(1)
I want to pass the data, because: I've got an NSDictionary within my ViewController. Now, I need the elements of this dictionary within the Tab Bar Controller to visualize certain values.Marcela
A
0

The easiest way is create a custom class and set the value from controller to custom class and then get the value from custom class to the controller.

Sample code is here:

First create custom class for example

AppUtility.h

+(void)setXxxName:(NSString *)str;
+(NSString *)getXxxName;

AppUtility.m

static NSString *xxxname;

+(void)setXxxName:(NSString *)str{

   xxxname=str;
 }

+(NSString *)getXxxName{

  return xxxname;
 } 

FirstController.m

import the custom class in the controller

#import "AppUtility.h"

set the value from first controller to custom class, use the below code to set the value:

[AppUtility setXxxName:@"xyz"];

Second controller.m

import the custom class in the controller

#import "AppUtility.h"

here get the value from the custom class, use the below code to get the value:

NSString *str = [AppUtility getXxxName];

In appUtility class Use nslog statements to verify the values are correct or not

Autocracy answered 19/11, 2013 at 6:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.