Pass a string between 2 view controllers - ios

My problem is quite simple, but as a beginner, I'm lost :D
I have to view controller :
view one call view 2 like this :
self.FacebookTypeRequest =#"favoris";
FaceBookViewController *viewcontrol = [[FaceBookViewController alloc]initWithNibName:#"FaceBookViewController" bundle:[NSBundle mainBundle]];
viewcontrol.title = #"FaceBook";
[self.navigationController pushViewController:viewcontrol animated:YES];
[viewcontrol release];
How can I send my string facebookTypeRequest to my view controller 2 ?

Make a property on your second view controller (FaceBookViewController) like this in the .h file :
#interface FaceBookViewController {
...
NSString *facebookTypeRequest;
...
}
#property (nonatomic, copy) NSString *facebookTypeRequest;
and in the .m file put
#implementation FaceBookViewController
#synthesize facebookTypeReqeust;
and remember to put in your dealloc method
- (void) dealloc {
[facebookTypeRequest release];
// release other properties here as well
[super dealloc];
}
Then, you can just set it like this :
self.FacebookTypeRequest = #"favoris";
FaceBookViewController *viewcontrol = [[FaceBookViewController alloc] initWithNibName:#"FaceBookViewController" bundle:nil];
viewcontrol.title = #"FaceBook";
viewcontrol.facebookTypeRequest = self.FacebookTypeRequest; //!< This is the line :)
[self.navigationController pushViewController:viewcontrol animated:YES];
[viewcontrol release];
Now, inside your FaceBookViewController you have the facebookTypeRequest.
Hope that helps.
NB It's generally bad practice to use captial letters to start the name of a property i.e. self.FaceboookTypeReqeust should really be self.facebookTypeRequest.

Related

Calling Story Board data - Objective C

I have an NSMutableArray that I am trying to access from one of my ViewControllers to the other.
Here is what I am doing:
a.h:
#import "b.h"
.....
#property(nonatomic,strong) b *viewControllers;
a.m:
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
_viewControllers = [storyboard instantiateViewControllerWithIdentifier:#"createChallengeInfo"];
.....code....
}
....code....
-(IBAction)sendVideo:(id) sender
{
NSLog(#"%#",self.viewControllers.selectedCells);
}
Now when logging this I encounter an error, EXC_BAD_ACCESS (code=1,address =0x13765350).
Here is my other ViewController.
b.h:
...code....
#property (strong, nonatomic) NSMutableArray *selectedCells; <-- this is the array I want to access
...code...
b.m:
#import "a.h"
...import...
#interface b ()
#end
#implementation b
- (void)viewDidLoad
{
[super viewDidLoad];
/* INITIATE VARIABLE */
_selectedCells = [[NSMutableArray alloc] init];
....code....
}
...code...
So my issue is accessing, selectedCells. But firstly why am I getting a EXC_BAD_ACCESS? And secondly before I get the error It lays out the ViewController and shows the variable selectedCells as nil.
Suggestions, thoughts?
If "b" is embedded in a container view of "a", then it is already instantiated when "a" gets instantiated. Therefore, when you use instantiateViewControllerWithIdentifier:, you are creating a new instance, NOT the one you have on screen. Your viewDidLoad should look like this instead,
- (void)viewDidLoad
{
[super viewDidLoad];
_viewControllers = self.childViewControllers[0];
.....code....
}
Another way to get a reference to the embedded view controller is in prepareForSegue, which will be called right after your "a" controller is instantiated. Your "b" controller will be segue.destinationViewController.

how to reach model from different view controllers?

i'm not new to OOP but i'm very new to objective c and i've been having trouble with reaching model from two different view controllers, one to set it the other to get the data. Here's my model:
#import <Foundation/Foundation.h>
#interface ModelUnit : NSObject{
NSString * nickname;
int total;
}
- (void)setTotal:(int)newTotal;
- (void)setName:(NSString *)nick;
- (NSString *)getName;
#end
#import "ModelUnit.h"
#implementation ModelUnit
- (void)setTotal:(int)newTotal{
total = newTotal;
}
- (void)setName:(NSString *)nick{
nickname = nick;
}
- (NSString *)getName{
return nickname;
}
#end
Here's how i try to set nickname in the initial viewcontroller:
//.h
#interface introViewController : UIViewController{
ModelUnit * modl;
}
-(ModelUnit *) modl;
-(IBAction)nickEntered:(UITextField *)sender;
#end
//.m
-(ModelUnit *) modl{
if(!modl){
modl = [[ModelUnit alloc] init];
}
return modl;
}
- (IBAction)nickEntered:(UITextField *)sender{
[[self modl] setName:[sender text]];
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self presentViewController:vew animated:NO completion:Nil];
}
And here is how i try to receive and display it in the last viewcontroller:
- (void)viewDidLoad
{
[super viewDidLoad];
introViewController *pnt = [[introViewController alloc] initWithNibName:nil bundle:nil];
[display setText:[[pnt modl] getName]];
}
But it always prints it as null when i NSlog it to the console. I know it's a very novice question but i'm completely stuck. Thanks for any help.
It's because each of your UIViewController instances are referencing different instances of your model class (actually, a non-instance in the second case).
Make an #property in your "last" view controller (class name of "ViewController" it would seem), something like:
#property (strong, nonatomic) Model *model;
Then, when pushing your second view controller, set its model property, like this:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
vew.model = self.modl;
[self presentViewController:few animated:NO completion:NULL];
That way, you're passing around the same instance of your Model class. The way you're doing it, you're trying to access a property that was never initialized, which is why you're getting nil.
If you're not new to OOP, you should quite easily see the mistake you're making. You're trying to retrieve a value out of an object you just initialized.
Where you need to pass the data is in the first object as you initialized the second object.
Between this line:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
And this line:
[self presentViewController:vew animated:NO completion:Nil];
You're still in the first object and you hold a reference to the second object. Give the ViewController class (a better name and) a NSString property to hold the data the getName method returns. And then set that property between the two lines I posted.
Also, Objective-C doesn't name their getters with get.

Using delegate to push data back

I use this code, but "it works" doesn't happen.
DetailViewController.h
[#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#protocol ProtocolNameDelegate
-(void)DoSomething;
#end
#interface DetailViewController : UIViewController {
id<ProtocolNameDelegate> _delegate;
}
#property (strong, nonatomic) id<ProtocolNameDelegate> _delegate;
DetailViewController.m
#synthesize _delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
[_delegate DoSomething];
}
MasterViewController.h
#interface MasterViewController : UITableViewController <ProtocolNameDelegate>
MasterViewController.m
-(void)DoSomething
{
NSLog(#"It works");
}
I think i have to add something like:
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
But it gives an error, and by the way will it create another instance of MasterViewController?
Instead of
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
write this,
DetailViewController* svc = [[DetailViewController alloc] init];
dvc._delegate = self;
You made mistake in implementation.
Abstract of implementation should be.
Create Protocol in DetailVC.
Create Property for Delegate, Synthesize, and make call.
Import DetailVC in MasterVC and include delegate in MasterVC.h
Implement protocol method in MasterVC.m
Create instance of DetailVC and assign DetailVCObj.delegate = self;
In MasterViewController.m, you need to allocate and intialise DetailViewController somewhere
DetailViewController* dvc = [[DetailViewControlleralloc] init];
dvc._delegate = self;
Also, because you have written [_delegate doSomething] in
DetailviewController's viewDidLoad method,
it means you must set dvc._delegate = self; in MasterViewController.m
before loading dvc's view (before addSubview or anything that loads view).

Add objects to an NSMutableArray in my base view controller from a second view controller, ios

I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...

Obj-C - How to pass data between viewcontrollers using a singleton?

Alright, so this is an extension to a question I asked last night. I have a little firmer grasp on how data can be passed between view controllers using various techniques. I wanted to go the MVC route, and creating a Singleton class seems the closest concept similar to MVC.
Basically I created a simple app with two View Controllers and a singleton class. I am trying to pass the value of a text field into a UILabel. For whatever reason it isn't working. This is what my code looks like.
ViewController.h
#import <UIKit/UIKit.h>
#import "Model.h"
#import "ViewController2.h"
#interface ViewController : UIViewController {
NSString *text2pass;
}
#property (weak, nonatomic) IBOutlet UITextField *tf;
#property (weak, nonatomic) IBOutlet UILabel *btn;
- (IBAction)go:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize tf = _tf;
#synthesize btn = _btn;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *tfstring = _tf.text;
NSLog(#"string = %#",tfstring);
}
- (void)viewDidUnload
{
[self setTf:nil];
[self setBtn:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)go:(id)sender {
NSLog(#"btn pressed");
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController2 *vc2 = (ViewController2 *) [storyboard instantiateViewControllerWithIdentifier:#"home"];
text2pass = _tf.text;
[self passValues];
[self presentModalViewController:vc2 animated:YES];
}
-(void) passValues {
Model *model = [Model sharedModel];
model.passedText = text2pass;
}
#end
ViewController2.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface ViewController2 : UIViewController {
NSString *passedText;
}
#property (nonatomic)NSString *passedValue;
#property (weak, nonatomic) IBOutlet UILabel *lbl;
- (IBAction)back:(id)sender;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
_lbl.text = passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Model.h
#import <Foundation/Foundation.h>
#interface Model : NSObject {
NSString *passedText;
}
#property (nonatomic, strong) NSString* passedText;
+ (Model *) sharedModel;
#end
Model.m
#import "Model.h"
#implementation Model
#synthesize passedText = _passedText;
static Model *sharedModel = nil;
+ (Model *) sharedModel {
#synchronized(self){
if (sharedModel == nil){
sharedModel = [[self alloc] init];
}
}
return sharedModel;
}
#end
The project can be downloaded in its entirety from here http://chrisrjones.com/files/KegCop-Test.zip
If you know why the UILabel is not displaying the text field text let me know. Oh I pretty much followed this -> http://www.youtube.com/watch?v=ZFGgMPcwYjg&feature=plcp
Your addressing, and memory management is just plain... off. Firstly, there's absolutely no reason to create a singleton for this, but that's beside the point here.
Secondly, when declaring properties, (atomic, assign) is defaulted to if not otherwise specified, which means your string:
#property (nonatomic)NSString *passedValue;
is weak sauce, ripe for deallocation and destruction at a moments notice. Declare it copy, strong, or retain.
Thirdly, there's absolutely no reference to your singleton in the pushed view controller, yet you seem to have the belief that objects that are named the same in different classes retain their value (especially when #import'ed). Not so. You need to reference your singleton and pull the value of [Model sharedModel].passedText into that text field.
In fact, I fixed your sample in two lines:
//ViewController2.m
#import "ViewController2.h"
//actually import the singleton for access later
#import "Model.h"
#interface ViewController2 () {
NSString *passedtext;
}
#end
#implementation ViewController2
#synthesize lbl = _lbl;
#synthesize passedValue = _passedValue;
- (void)viewDidLoad
{
// do code stuff here
NSLog(#"passedText = %#",passedText);
//actually reference the singleton this time
_lbl.text = [Model sharedModel].passedText;
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setLbl:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (IBAction)back:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ViewController *vc = (ViewController *) [storyboard instantiateViewControllerWithIdentifier:#"welcome"];
[self presentModalViewController:vc animated:YES];
}
#end
Which yields this:
I wouldn't recommend using a Singleton as a good way to pass data around your application. Most apps are simple enough that this kind of central access is not necessary, and it usually creates a maintenance nightmare... but I don't think the fact that you're using a Singleton is actually important to getting your code working.
Assuming you have access to the data in ViewController1, in your case through the a Singleton instance of Model (which needs a more descriptive name), then all you have to do is pass through the data to ViewController2 when it is created and presented, which eliminates the need for a Singleton at all.
Once you create the controller, set the data you need, and then present the view controller - which is basically what you're doing anyway.
As to why it's not working: Is the view controller being presented, just not with the correct data? Or is there actually an issue presenting the controller at all? I would set a breakpoint in the go: action of ViewController1, make sure the data you expect is in the textfield, correctly populates the Model and that the value is correctly pulled out of the Model in ViewController2.
Unless you've removed some of the code, it looks like you correctly populate the Model property in ViewController1, but in ViewController2 you refer to a local ivar passedTextrather than pulling it from the model.
On a separate note, the way to go back from a presented modal view controller is usually to dismiss that controller, not to re-create the initial controller and present that over the top.

Resources