I have an iPhone app with three pages, each of which allows the user to enter some text. On the final page I want to concatenate all three strings and print it out. I have a UIViewController (named PageXController) for each page and I am trying to pass variables along to the final page. The method I currently try doesn't quite work. Here is an example:
I begin by declaring a string as an instance variable in PageThreeController.h
#interface PageThreeController : UIViewController{
NSMutableString *string;
}
#property (nonatomic) NSMutableString *string;
Next I add the following to PageOneController.h,
#import "PageThreeController.h"
#interface PageOneController : UIViewController
#property (nonatomic) PageThreeController *pageThree;
In PageOneController I then attempt to set the string instance variable on page three;
- (IBAction)handleButton:(id)sender {
_pageThree = [[PageThreeController alloc] init];
_pageThree.from = [[NSMutableString alloc]init];
[_pageThree.from appendString:#"Hello World"];
NSLog(#"My string is %# on page one.", _pageThree.from);
}
The NSLog prints out My string is 'Hello World' on page one. but when I add the same NSLog on PageThreeController.m before concatenating, 'string' is NULL.
It seems that I am making a separate copy of the pageThreeViewController. What do I need to do to change the actual value of string on page three? I am really new at this
The easiest way to pass data between ViewControllers is using the AppDelegate, even though
there is other methods .
Method 1 - using AppDelegate.
Add the following line in your Appdelegate.
#property (strong,nonatomic) NSMutableString *str;
To access the variable from any view controller,
MyAppdeleagte appDelegate=[[UIApplication sharedApplication]delegate];
NSMutableString *Stringdata=appDelegate.str;
Method 2 -Specifying objects.
in this method , you can proceed as you are now doing and, just need to specify the
view controller instance.
let you have are navigating from one controller to another , say FirstController to Second.
Firstcontroller.h
#interface FirstController : UIViewController{
NSMutableString *string;
}
#property (nonatomic) NSMutableString *string;
SecondController.h
#interface SecondController : UIViewController{
}
#property (strong,nonatomic) FirstController *firstScreen;
Within your implementation of the FirstController, before you navigate to the SecondController ,you have to specify the instance in SecondController.
in FirstController.m
SecondController *nextScreen=[self.storyboard instantiateViewControllerWithIdentifier:#"SecondView"];
nextScreen.firstScreen=self;
Then in your SecondController.m , you can simply get the String as
_firstScreen.string;
Customize your prepareForSegue method in order to pass necessary vars through public properties of these controllers.
For example:
#pragma mark - PreparaForSegue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"goToDetail"]) {
YourDestinationController *destinationController = segue.destinationViewController;
destinationController.myString = self.myStringToPass;
}
}
Where myString is a public property in YourDestinationController and myStringToPass is a property in your source controller (it could be in private scope)
If you need to access it from anywhere, you could store the variable in the AppDelegate. So if you had a variable like this in your AppDelegate:
#property (nonatomic) NSString *currentStringToPass;
Then you could access it from your ViewControllers by using the following code:
- (IBAction)handleButton:(id)sender {
AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
_pageThree = [[PageThreeController alloc] init];
_pageThree.from = [[NSMutableString alloc]init];
[app setCurrentStringToPass:#"Hello World"];
NSLog(#"My string is %# on page one.", [app currentStringToPass]);
}
Related
This question already has answers here:
How to pass prepareForSegue: an object
(10 answers)
Passing data between view controllers
(45 answers)
Closed 6 years ago.
I have TableViewController & ViewController.
Airports.h
#interface Airports : UITableViewController
#property(strong, nonatomic) NSNumber* latitude;
#property(strong, nonatomic) NSNumber* longitude;
#property(strong, nonatomic) NSString* selectedAirportName;
#end
Airports.m
Here is an instance that sets latitude, longitude & selectedAirportName properties.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray *airportNames = airportsArray[indexPath.row];
self.selectedAirportName = [airportNames valueForKey:#"$"];
NSArray *airportCoordinates = airportsCoordinates[indexPath.row];
self.latitude = [airportCoordinates valueForKey:#"Latitude"];
self.longitude = [airportCoordinates valueForKey:#"Longitude"];
}
Row selection forwards user to ViewController where I have an instance that shows GoogleMap with marker accordingly to information in selected row. How to give that instance access to latitude, longitude & selectedAirportName properties from Airports class? Actually, I wonder how can I make that properties public to operate with them out of didSelectRowAtIndexPath instance and out of Airports class.
I'm only a beginner. So don't shoot me :)
You should create a seperate object that maps the data you wish to pass to the viewcontroller with a google map, maybe call it something like Airport.
Then when you push the segue from the first view controller(Airports), you can pass this object to the view controller in the prepareforsegue method.
How to pass prepareForSegue: an object
In your ViewController.h have it look like this:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property NSNumber* airportLatitude;
#property NSNumber* airportLongitude;
#end
Then in your Airports.m, when you instantiate your ViewController, you can set the values of ViewController like this:
ViewController *view = [[ViewController alloc] init];
view.airportLatitude = [NSNumber numberWithInt:1];
view.airportLongitude = [NSNumber numberWithInt:1];
Code:
[[UIApplication sharedApplication] delegate];
Through this code how to pass data from one to another.
Basically there is only one instance of 'sharedApplication' (it is a singleton), which means that instances of view controllers that are independent of each other can still talk to the same data object. You can therefore write methods in your sharedApplication delegate which can effectively allow these two view controllers to communicate. For more information please see the Apple documentation.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/
You can use the segue destination view controller for transfar data between controllers.Suppose you have one TextField in one View Controller and you want to pass the Textfield text to another viewController.Then use this it might be helpful.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
TargetViewController *tvc; //Second view controller Object
tvc = [segue destinationViewController];
tvc.lonetxt = fname.text; //use properties of another view using . operator
tvc.ltwotxt = lname.text;
tvc.lthreetxt = age.text;
tvc.lfourtxt = college.text;
}
Try following:
Interface in Your AppDelegate:
#interface MyAppDelegate : NSObject {
NSString *myString;
}
#property (nonatomic, retain) NSString *myString;
...
#end
and in the .m file for the App Delegate you would write:
#implementation MyAppDelegate
#synthesize myString;
myString = some string;
#end
Then, in viewcontroller.m file you can fetch:
MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate];
someString = appDelegate.myString; //..to read
appDelegate.myString = some NSString; //..to write
I'm trying to pass an NSString from ViewController to secondViewController, but the value returns null. I'm using Storyboards and a present modally segue that fires when the selectDateButton is pressed.
The user selects a date using a UIDatePicker, the value is then formatted to MM/dd and put into an NSString ivar. I'm taking that ivar and setting it to a property in my secondViewController. I'm pretty sure my mistake's probably in the way I've written it in the prepareForSegue section.
I've also tried writing it as svc.theDate = [NSString stringWithFormat:#"%#", selectedDateString]; to no avail.
I have no idea why this is happening and would appreciate some help.
ViewController.h
#import "secondViewController.h"
#interface ViewController : UIViewController {
UIDatePicker *datePicker;
NSDateFormatter *dateFormatter;
NSDate *selectedDate;
NSString *selectedDateString;
}
ViewController.m
- (IBAction)selectDateButton:(id)sender {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM/dd"];
selectedDate = [datePicker date];
selectedDateString = [dateFormatter stringFromDate:selectedDate];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"toSecondViewController"]) {
secondViewController *svc = [segue destinationViewController];
svc.theDate = selectedDateString;
}
}
secondViewController.h
#import "ViewController.h"
#interface secondViewController : UIViewController {}
#property (nonatomic, strong)NSString *theDate;
- (IBAction)button:(id)sender;
#end
secondViewController.m
- (IBAction)button:(id)sender {
NSLog(#"Selected Date: %#", _theDate);
}
The problem is that instance variables in Objective-C by default have the
#protected
storage qualifier. That means only the class itself and its subclasses can access the instance variable.
However, what you are actually doing in prepareforSegue is this:
svc.theDate = selectedDateString;
i.e. you are passing a reference to the selectedDateString instance variable via a setter method to the BirthDayScreen view controller instance, which does not have access to selectedDateString instance variable. Why? Because it is not related in any way to the ViewController class and the variable itself is not public.
Later when you try to possibly show the string in UI, you actually have a reference to the ivar in the ViewController instance but it is not accessible so you get null.
You could explicitly make it public in ViewController
#interface ViewController : UIViewController {
UIDatePicker *datePicker;
NSDateFormatter *dateFormatter;
NSDate *selectedDate;
#public NSString *selectedDateString;
}
But actually this is not the right approach.
Instead, try changing the storage qualifier for the theDate property to
#property (nonatomic, copy)NSString *theDate;
This will create a new instance of NSString that will be a copy of the NSString represented by the selectedDateString variable, but this new copied instance will belong to the SecondViewController.
If it is not enough and still not work (it should) , instead of having instance variables in the ViewController header, create a proper public property called selectedDateString.
#interface ViewController : UIViewController {
UIDatePicker *datePicker;
NSDateFormatter *dateFormatter;
NSDate *selectedDate;
}
#property (nonatomic, strong) NSString *selectedDateString;
This way you will expose the value of the backing _selectedDateString to the outside world.
#import "AppDelegate.h"
#property (nonatomic,strong) NSString *theDate;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *str = [NSString StringWithFormate:#"%#",appDelegate.imageStringName];
NSLog(#"str : %#",str);
}
I have an NSMutableArray declared as property in .h and initialized in viewDidLoad in my SPOCVC .m (UIViewController)...
#property (strong, nonatomic) NSMutableArray* SPOCTrackList;
in viewDidLoad
if ([self SPOCTrackList] == nil) {
self.SPOCTrackList = [[NSMutableArray alloc]init];
NSLog(#"SPOTTrackList INITIALIZED");
}
In a separate VC, I'm trying to pass/addObject to SPOCTracklist...
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SCTrack* selectedTrack = self.trackList[indexPath.row];
[[[SPOCVC sharedInstance]SPOCTrackList]addObject:selectedTrack];
NSLog(#"%lu", (unsigned long)[[[SPOCVC sharedInstance]SPOCTrackList]count]);
So my NSMutableArray is initialized and I can add dummy objects, but why can't I pass it from another VC using singleton or anything, such as...
SPOCVC* spocVC = self.tabBarController.viewControllers[2];
[spocVC.SPOCTrackList addObject:selectedTrack];
Thanks for pointing me in the right direction.
View controllers are only intended to be around while they are on screen. They are not a place to store data. Generally when one view controller talks directly to another view controller that it didn't create, you're doing something wrong.
Move SPOCTrackList to your model and have both view controllers talk to it rather than to each other.
There should never be a "sharedInstance" on a view controller. That's a sure sign that you're abusing the view controller as the model.
What's probably happening in your particular case is that viewDidLoad is running on a completely different SPOCVC than your sharedInstance.
why not use appdelegate to handle this
appdelegate.h
//add property to hold the reference
#property (nonatomic, copy) NSMutableArray *referenceArray;
//share the app delegate
+(AppDelegate *)sharedAppDelegate;
#end
in appdelegate.m
//synthesize the property
#synthesize referenceArray;
//return the actual delegate
+(AppDelegate *)sharedAppDelegate {return (AppDelegate *)[UIApplication sharedApplication].delegate;}
in viewdidload method
//add the delegate
import "appdelegate.h"
//init the array
self.SPOCTrackList = [[NSMutableArray alloc]init];
//Add reference
[AppDelegate sharedAppDelegate].referenceArray = self.SPOCTrackList;
and add anywhere like this
import "appdelegate.h"
[[AppDelegate sharedAppDelegate].referenceArray addobject:object];
I am using iOS 5 SDK with arc. I want to pass NSObject from VC1 view controller to VC2 view controller, do modification and put back into VC1 controller. I don't want to point same nsobject from both VC1 and VC2 controllers. Whenever I am passing nsobject, it should create a copy of that nsobject and do the modification. (Do not modify actual nsobject).
I have tried below code but it crashes and giving error as
-[ImageObject mutableCopyWithZone:]: unrecognized selector sent to instance 0x1364ec20
Code:
I have NSObject as :
#interface ImageObject : NSObject
#property (copy,nonatomic) NSString *path;
#property (nonatomic, readwrite) int id;
#end
In VC1 view controller:
I am passing my nsobject to VC2 view controller as follows:
VC2ViewController *vc2 = [[VC2ViewController alloc] initWithNibName:#"VC2ViewController" bundle:nil];
vc2.imageObj = [imgObj mutableCopy];
[self.navigationController pushViewController:vc2 animated:YES];
In VC2 view controller:
VC2ViewController.h file
#import "ImageObject.h"
#interface VC2ViewController : UIViewController
#property (retain,nonatomic) ImageObject *imageObj;
#end
VC2ViewController.m file
// modifying nsobject as below
-(void)modifyObject
{
UIViewController *previousViewController = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
if ([previousViewController isKindOfClass:[VC1ViewController class]])
{
VC1ViewController *parent = (VC1ViewController *) previousViewController;
if(parent != nil)
{
_imageObj.id = 2;
[parent reloadData:_imageObj];
}
parent = nil;
}
[self.navigationController popViewControllerAnimated:YES];
}
Have any idea ? How to resolve this issue?
Your ImageObject class needs to conform to the NSCopying Protocol.
This answer here explains better and shows you how the code looks like.
I also think that you need to use [imgObj copy] instead of [imgObj mutableCopy] because according to apple docs:
The NSMutableCopying protocol declares a method for providing mutable copies of an object.
Only classes that define an “immutable vs. mutable” distinction should adopt this protocol.
Classes that don’t define such a distinction should adopt NSCopying instead.