Trying to pass data between viewControllers - ios5

I have a sequence of 4 viewControllers inside a NavigationController, each grabs a few textFields of input from the user which are stored in a NSMutableDictionary.
Each of the VC's set's itself up as the delegate of the nextVC before it segues, it also passes the NSMutDict along.
This works fine.
What I don't understand is this:
Say I have filled in the 5 textFields in VC1. Then I set myself as the delegate of VC2, pass VC2 the dictionary with the input data and segue to VC2. In VC2 I fill in another 4 textFields and add these to the dictionary. If I then decide I need to change something in VC1 I tap the back button and amend the data. But when I go forwards again I lose the stuff I input on VC2.
How do I pass the dictionary back to VC1 with the added info so that when it gets passed forwards to VC2 again it has everything in it?
The delegate (VC1) has a method to update its dictionary with the dictionary in VC2.
I have also customised the backBarButtonItem in VC2 by setting it in the prepareForSegue: method in VC1.
I think I'm getting close but...
I can only get the target actions to work by setting a leftBarButtonItem in VC2 and using that instead of the default back button.
Setting the back button in VC1 (prepareForSegue:) doesn't seem to allow any target or action to be set.
I know I can't set the back button in VC2, so what can I do? Can I set the target and action of the back button from VC2 using the delegate?
I think it may be something to do with UINavigationBarDelegate but I can't figure out where to put what with that. I tried setting it up in VC2 but it didn't do anything.
TIA.
Here's the relevant code:
Protocol:
#import <Foundation/Foundation.h>
#protocol IAXAddNewUserDelegate <NSObject>
#required
- (void)updateNewUserDataWithData: (NSMutableDictionary *)newData;
#end
From VC1.h:
#import "IAXAddNewUserDelegate.h"
#interface IAXAddNewUser1 : UITableViewController <UITextFieldDelegate, UIAlertViewDelegate, IAXAddNewUserDelegate>
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
#property (strong, nonatomic) User *selectedUser;
#property (strong, nonatomic) User *aNewUser;
#property BOOL isFirstUser;
- (void)updateNewUserDataWithData: (NSMutableDictionary *)newData;
#end
From VC1.m:
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddUser2"]) {
IAXAddNewUser2 *addUser2VC = segue.destinationViewController;
addUser2VC.managedObjectContext = self.managedObjectContext;
addUser2VC.progressTotal = self.progressTotal;
addUser2VC.isFirstUser = self.isFirstUser;
addUser2VC.userData = self.userData;
addUser2VC.delegate = self;
if (self.selectedUser) {
addUser2VC.selectedUser = self.selectedUser;
}
self.title = #"Step 1";
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(passDataBack:)];
self.navigationItem.backBarButtonItem = backButton;
}
}
#pragma mark - IAXAddNewUserDelegate Methods
- (void)updateNewUserDataWithData: (NSMutableDictionary *)newData
{
self.userData = newData;
NSLog(#"Updated AddUserVC1");
}
From VC2.m
-(void)passDataBack:(id)sender
{
NSLog(#"Sending Data Back to VC1");
[self.delegate updateNewUserDataWithData:self.userData];
[self.navigationController popViewControllerAnimated:YES];
}

If you're updating all the dictionaries from all the other dictionaries, try using a singleton. You can see an example here: https://stackoverflow.com/a/9690731/542400
Also, here's some code:
MainDictionary.h
#interface MainDictionary : NSObject{
NSMutableDictionary *dictionary;
}
+(MainDictionary *)sharedDictionary;
-(NSString *)getStringForKey:(NSString *)string;
-(void)setString:(NSString *)string forKey:(NSString *)key;
#end
MainDictionary.m
#import "MainDictionary.h"
static MainDictionary *sharedDictionary;
#implementation MainDictionary
-(id)init{
self = [super init];
dictionary = [[NSMutableDictionary alloc] init];
// if you want to add anything preliminary to the dictionary, do it here
return self;
}
+(MainDictionary *)sharedDictionary{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedDictionary = [[self alloc] init];
});
return sharedDictionary;
}
-(NSString *)getStringForKey:(NSString *)string{
return [dictionary objectForKey:string];
}
-(void)setString:(NSString *)string forKey:(NSString *)key{
[dictionary setValue:string forKey:key];
}
#end
Now #import MainDictionary.h, and any time you want to access or set values in that dictionary (in this example, when your textFields end editing), just do this:
-(void)textFieldDidEndEditing:(UITextField *)textField{
if(textField == textField1){
[[MainDictionary sharedDictionary] setString: textField.text forKey:#"textField1"];
}
}
or:
-(void)viewWillAppear{
textField1.text = [[MainDictionary sharedDictionary] getStringForKey:#"textField1"];
[super viewWillAppear:YES];
}
Implement this in each VC, and you're good to go.

Related

iPhone: How do I share information between my UITabBarViewController and the individual Tabs?

I have a TableViewController that segues into a TabBarViewController.
I know how to pass my object via a segue, but not by a relationship like the TabBarViewController and it's tab share.
How can I do this? From the TabView is there a way to access the TabBarViewControllers member variables?
Update:
This is how I've solved the problem so far, but I'm not crazy about using the AppDelegate to do this...
Add the Following to WhateverYouNamedYourAppDelegate.h
#class myObjectIWantToPass;
#property (strong, nonatomic) myObjectIWantToPass *object;
Then add the following to the View Class file you have your data in that you want to pass on. I'm going to assume you know how to set up your object already in this file if your planning on passing it to another view.
WhateverYouNamedYourAppDelegate *appDelegate =
(WhateverYouNamedYourAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.object = object;
Then you do some similar work to retrieve the object back from the appDelegate in your destination View Class.
WhateverYouNamedYourAppDelegate *appDelegate = (WhateverYouNamedYourAppDelegate *)[[UIApplication sharedApplication] delegate];
object = appDelegate.object;
You can make singleton classes, so that all of the controllers can access those variables in the Singleton. See Code below
SingletonClass.h
#interface SingletonClass : NSObject {
NSString *someString;
}
#property(nonatomic, retain)NSString *someString;
+(id)shared;
#end
SingletonClass.m
#import "SingletonClass.h"
static SingletonClass *aShared;
#implementation LibShared
#synthesize someString;
+(id)shared
{
if (aShared == nil) {
aShared = [[self alloc] init];
}
return aShared;
}
-(id)init
{
if (self = [super init]) {
}
}
-(void)dealloc
{
[someString releease];
[super dealloc];
}
In the tabbar you can set the variable on SingletonClass:
[[SingletonClass shared] setSomeString:#"Value_Set"];
On the tableViewController, you can get the property of the someString variable on the SingletonClass:
NSString *string = [[SingletonClass shared] someString];
There's no need for a singleton pattern here. Instead, you can send the data-object forwards in - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender just as you do normally, you just need to find the correct viewController in the UITabBarController's viewControllers property.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"MyTabBarSegue]) {
UITabBarController *tabBarController = segue.destinationViewController;
// Either set the index here, if you know for sure which viewController is which, or
// Enumerate the viewControllers for isKindOfClass:[MYCustomViewController class] to be robust and change-proof
MYCustomViewController *myVC = [[tabBarController viewControllers] objectAtIndex:0];
myVC.dataObject = self.dataObject;
}
}

iOS - Passing variable to view controller

I have a view with a view controller and when I show this view on screen, I want to be able to pass variables to it from the calling class, so that I can set the values of labels etc.
First, I just tried creating a property for one of the labels, and calling that from the calling class. For example:
SetTeamsViewController *vc = [[SetTeamsViewController alloc] init];
vc.myLabel.text = self.teamCount;
[self presentModalViewController:vc animated:YES];
[vc release];
However, this didn't work. So I tried creating a convenience initializer.
SetTeamsViewController *vc = [[SetTeamsViewController alloc] initWithTeamCount:self.teamCount];
And then in the SetTeamsViewController I had
- (id)initWithTeamCount:(int)teamCount {
self = [super initWithNibName:nil bundle:nil];
if (self) {
// Custom initialization
self.teamCountLabel.text = [NSString stringWithFormat:#"%d",teamCount];
}
return self;
}
However, this didn't work either. It's just loading whatever value I've given the label in the nib file. I've littered the code with NSLog()s and it is passing the correct variable values around, it's just not setting the label.
Any help would be greatly appreciated.
EDIT: I've just tried setting an instance variable in my designated initializer, and then setting the label in viewDidLoad and that works! Is this the best way to do this?
Also, when dismissing this modal view controller, I update the text of a button in the view of the calling ViewController too. However, if I press this button again (to show the modal view again) whilst the other view is animating on screen, the button temporarily has it's original value again (from the nib). Does anyone know why this is?
When a view controller is being initialized, inside the initWithNibName method, the views that reside in the view controller aren't yet initialized and you can't set their properties yet. Do whatever you need that is view based in the "viewDidLoad" method.
I am not a pro but this may help you.
In the header view1.h, declare the desired property:
// view1.h
#interface view1 : UIViewController {
NSString *passingVariable;
}
#property (nonatomic, strong) NSString *passingVariable;
#end
and then in the implementation of view1, synthesize the variable:
// view1.m
#implementation view1
#synthesize passingVariable;
// the rest of the implementation
#end
and, finally in the implementation of the other view controller, view2:
// view2.m
#import "view1.h"
#implementation view2
-(IBAction)changeview
{
view1 *myview = [[view1 alloc] init];
myview.passingVariable = #"Hello Variable";
[self.navigationController pushViewController:myview animated:YES];
}
#end
here i am trying to move from view2 to view 1 and also initializing the passingVariable ivar of view1. hope this will help you.
Here i'm passing the ViewController's label text to SecondViewController's Label Text
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
{
// please make your control on XIB set these IBOutlet's
//I'm not showing how to connect these with XIB
IBOutlet UILabel *lblView;
IBOutlet UIButton *buttonGo;
}
//this is method which will push the view
-(IBAction)buttonGoClickAction:(id)sender;
ViewController.m
-(IBAction)buttonGoClickAction:(id)sender
{
SecondViewController *secondViewObject = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
//before pushing give the text
secondViewObject.string = lblView.text;
[self.navigationController pushViewController:secondViewObject animated:YES];
}
SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController
{
IBOutlet UILabel *labelView;
NSString *string;
}
//set the string property
#property(nonatomic, retain) NSString *string;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
//synthesize string here
#synthesize string;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
//Here you will get the string
labelView.text = string;
}
Firstly you check that have you attach this label IBOutlet in xib or not if you made it via Interface Builder....
use it like this....
SetTeamsViewController *vc = [[SetTeamsViewController alloc] initWithTeamCount:teamCount];
Take a string variable in .h file and set that string here .. NSSting *str in .h
- (id)initWithTeamCount:(int)teamCount {
self = [super init];
if (self) {
// Custom initialization
str = [NSString stringWithFormat:#"%d",teamCount];
}
return self;
}
and set your label in viewDidLoad: or in viewWillApear:
self.teamCountLabel.text = str;
May this will help you
As said by stavash, control in the xib are created in the view did load. To be more precise, they are created with that line :
[super viewDidLoad];
So, mylabel doesn't exist before that time (it is nil).
The easiest way is to do that :
SetTeamsViewController *vc = [[SetTeamsViewController alloc] init];
[self presentModalViewController:vc animated:YES];
vc.myLabel.text = self.teamCount;
[vc release];
The longer but more correct path is to have a member NSString* in SetTeamsViewController class, to set it to teamCount before showing the window, and in the view did load to put that membre value in your label.
Cdt
It depends on your need. You can use Singleton class for sharing of your variables between different classes. Define all variable which you wants share in your DataClass.
in .h file (where RootViewController is my DataClass, replace name with your new class)
+(RootViewController*)sharedFirstViewController;
in .m file
//make the class singleton:-
+(RootViewController*)sharedFirstViewController
{
#synchronized([RootViewController class])
{
if (!_sharedFirstViewController)
[[self alloc] init];
return _sharedFirstViewController;
}
return nil;
}
+(id)alloc
{
#synchronized([RootViewController class])
{
NSAssert(_sharedFirstViewController == nil,
#"Attempted to allocate a second instance of a singleton.");
_sharedFirstViewController = [super alloc];
return _sharedFirstViewController;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
after that you can use your variable in any other class like this
[RootViewController sharedFirstViewController].variable
Hope it's help you:)
With Storyboards the the right way is to pass the indexPath as sender argument in performSegueWithIdentifier
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"segueIdentifier" sender:indexPath];
}
and to set a property in the destination controller:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString: #"segueIdentifier"]) {
NSIndexPath *indexPath = sender;
DetailViewController *dest = [segue destinationViewController];
dest.usersArray = [self.usersArray objectAtIndex:indexPath.row];
}
}
What I have done whenever I needed another class to have the variables from the previous class I either set up a global class that will store the values incase I need them in more locations or in the interface you can set #public variables. These variables can be set using the controller that you created for the next view as such.
controller->pub_var1 = val1;
controller->pub_var2 = val2;
This will be done before you pass the view to the root controller or just before you call the next view. You will need to #import "class.h" so that you can access those public variables.
I can show code if this is not clear

Present view from delegate modally in iOS 5

I cannot seem to find this anywhere online. I have an add button in one of my views and I have hooked it up to an IBAction method called add. In my storyboard, I have created a view that has a form all set up on it. I have assigned a class to that view in the storyboard as well. That class is called AddItemViewController.
I am trying to present this view modally and then set the delegate to the view that called the AddItemViewController. However, all I get is an empty UITableViewController that shows up. Here is my code that I'm trying to use:
- (IBAction)add {
AddItemViewController *addItem = [[AddItemViewController alloc] init];
addItem.delegate = self;
[self presentModalViewController:addItem animated:YES];
}
Is there anything I'm missing? Why does it just show an empty table and not the view controller that I set up in the storyboard?
Here is the code from the AddItemViewController:
#interface AddItemViewController : UITableViewController <UITextFieldDelegate> {
}
#property (strong, nonatomic) IBOutlet UITextField *note;
- (void)save:(id)sender;
- (void)cancel:(id)sender;
#end
#implementation AddItemViewController
- (void)viewDidLoad {
}
- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)save:(id)sender {
DbHandler *db = [[DbHandler alloc] init];
[db executeUpdate:self.note];
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
Well, AddItemViewController inherits from UITableViewController, not UIViewController, so it makes sense that a UITableViewController is showing up.
You should initiate the AddItemViewController like this:
AddItemViewController *addItem = [[AddItemViewController alloc] initWithNibName:#"AddItemViewController"];

Passing of UITextField text from one view to another

I defined a UITextField on my firstViewController as follow
// firstViewController.h
IBOutlet UITextField *PickUpAddress
#property (nonatomic, retain) UITextField *PickUpAddress;
//firstViewController.m
#synthesize PickUpAddress;
// Push secondView when the 'Done' keyboard button is pressed
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
if (textField == PickUpAddress) {
SecondViewController *secondViewController= [[SecondViewController alloc]
initWithNibName:#"SecondViewController"
bundle:nil];
secondViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:secondViewController animated:YES];
[secondViewController release];
}
return NO;
}
Then I tried to retrive it in my secondViewController during viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
BookingViewController *bookingViewController = [[BookingViewController alloc] init];
NSString *addressString = [[NSString alloc] init];
addressString = bookingViewController.PickUpAddress.text;
NSLog(#"addressString is %#", bookingViewController.PickUpAddress.text);
}
But it returns as NULL on my console. Why is that so?
Thanks in advance :)
in secondViewController.h add
NSString *text;
#property (nonatomic, retain) NSString *text;
-(void)setTextFromText:(NSString *)fromText;
in secondViewController.m add following
- (void)setTextFromText:(NSString *)fromText
{
[text release];
[fromText retain];
text = fromText;
}
in firstViewController.m
before
[self.navigationController pushViewController:secondViewController animated:YES];
add
[secondViewContoller setTextFromText:PickUpAddress.text];
Now let me explain the code.
You are adding an NSString to second view , where we will store the text from the UITextField. Then, we've written a method, which will set that NSString from some other NSString.
Before pushing secondViewController to navigationController, you're just calling that method to set our text from PickUpAddress.text.
Hope that helped.
Problem is in your code. You are creating new object, bookingViewController, to retrieve the textField value. So it will obviously provide NULL. Rather you should use one unique object application wide to access the value.

How to assign managedObjectContext to a dynamic viewController?

I have 4 buttons on main screen, each one sends me to a viewController. The third one, sends me to a view on which I wanna set the managedObjectContext. If I use the class name to create an instance, it's all right. But I'm looking for a way to use just one method that uses an array to retrieve the name of the Class for the needed viewController. But it's leading to an error message, like it doesn't exist on the destination viewController??? Anyone have any ideas about this aproach??? Thanks in advance!
Here is the code:
NSArray *viewControllers = [[NSArray alloc]
initWithObjects:#"nil",#"OpcoesView",#"nil",#"TheNames", nil];
NSString *viewName = [viewControllers objectAtIndex:[sender tag]]; //the taped button tag
UIViewController *viewController = [[NSClassFromString(viewName) alloc]
initWithNibName:viewName bundle:nil];
if ([sender tag] == 3) {
viewController.managedObjectContext = contexto;
}
You do not need to know the subclass at all. Because Objective-C is a dynamic language and messages are resolved at runtime, you can send the message without having to know anything about the subclass at all.
First I would refer to the subclass as an id (instead of UIViewController) and as long as you have its header imported you can call [viewController setManagedObjectContext:contexto] directly.
However if you don't want to or can't import the header then just use KVC as follows:
[viewController setValue:contexto forKey:#"managedObjectContext"];
I would keep MOC in my app delegate instead of assigning it down to every of my viewControllers:
And in my viewController .m file:
#import "MyAppDelegate.h" // Assuming you have a property called managedObjectContext in your MyAppDelegate
#interface MyViewController (PrivateMethgods)
#property (nonatomic, readonly) NSManagedObjectContext * managedObjectContext;
#end
#implementation MyViewController
#dynamic managedObjectContext
- (NSManagedObjectContext *)managedObjectContext {
MyAppDelegate *appDelegate = (MyAppDelegate *)[UIApplication sharedApplication].delegate;
return appDelegate.managedObjectContext;
}
So I can use it in my viewController like this:
if ([self.managedObjectContext hasChanges]) {
...
}
To set a property that is only in the subclass view controller (such as "managedObjectContext"), you can take advantage of the fact that you know the type like this:
NSArray *viewControllerNames = [[NSArray alloc] initWithObjects:#"nil",#"OpcoesView",#"nil",#"TheNames", nil];
NSString *viewControllerName = [viewControllerNames objectAtIndex:[sender tag]]; //the tapped button tag
UIViewController *viewController = [[NSClassFromString(viewControllerName) alloc] initWithNibName:viewControllerName bundle:nil];
if ([sender tag] == 3) {
TheNames *namesVC = (TheNames*)viewController;
namesVC.managedObjectContext = contexto;
}