I'm setting a string in a view controller called ViewController and trying to access it somewhere else. This is the code:
ViewController.h
NSString *string;
...
#property (retain) NSString *string;
ViewController.m
#synthesize string;
...
-(void)viewDidLoad {
...
string = #"Test";
}
OtherViewController.m
#import "ViewController.h"
...
-(void)viewDidLoad {
ViewController *vc;
vc = [[ViewController alloc] init];
NSLog(#"String: %#", vc.string);
}
However, the log is showing: String: (null). What am I doing incorrectly? Thanks.
The viewDidLoad of ViewController is only called when the view is loaded. The view is lazily loaded when required e.g. when a call to vc.view is made.
I'm not sure what you are trying to achieve but this certainly seems like a code smell to me.
As #Fscheidl points out you are creating a new instance and not accessing an existing instance so this may add to your problem. I still believe your main issue is that you assume viewDidLoad is being called just by creating the viewController, which is not the case
edit : it doesn't necessarily need to be an NSObject class, if you want to, you could also do this on your viewController class, just be sure to also include
-(id)init
on your header
---- end of edit
if you're trying to make a class that's accessible to another view controller, why not try NSObject instead of view controller (considering you only need to take that string value)
for instance, lets call that viewController class "global" class
so at global.h, you put up
#import <Foundation/Foundation.h>
#interface GlobalVar : NSObject
#property (nonatomic, strong) NSString *myString;
-(id)init;
#end
and then, at global.m you put up
#import "GlobalVar.h"
#implementation GlobalVar
#synthesize myString;
-(id)init
{
self = [super init];
if(self)
{
myString = [[NSString alloc]initWithFormat:#"the String"];
}
return self;
}
#end
after this, everytime you need to access the "myString" object that contained in global class, you could put up
at header :
#import "GlobalVar.h"
...
...
#property (nonatomic, strong) GlobalVar *globalVar;
at implementation file :
#synthesize globalVar;
...
...
self.globalVar = [[GlobalVar alloc]init];
NSString *theString = globalVar.myString;
NSLog(#"content of my string is : %#",theString);
there you go ;)
You do create a new instance of ViewController by calling [[ViewController alloc] init]; This instance hasn't had string even set. You have to access that exact instance of ViewController.
If you create the instance of OtherViewController directly from ViewController, you can add the following to OtherViewController.h:
#import "ViewController.h"
#property (nonatomic, retain) ViewController *previousViewController
When creating the OtherViewController, you can then set:
//alloc and init instance of OtherViewController
myOtherViewController.previousViewController = self;
In your viewDidLoad: method, you can then access your string as follows:
NSLog(#"String: %#", previousViewController.string);
Related
I'm a relatively new iPhone developer and am making great progress building my 2nd iPhone app. In the app I'm building now I'm doing some code separation with some protocols and delegates so that I car re-use some of my code in a variety of places throughout my code.
Here's what I want to happen:
CITRootViewController creates an instance of a CITReportCreator class, passing itself as a property so that the reportCreator can open additional view controllers and such.
CITReportCreator class is declared as implementing the CITImageCaptureDelegate protocol, which is declared in the CITImageCaptureViewController file.
CITImageCaptureViewController defines the delegate protocol and has a method that passes back data and references to the child view controller so that CITReportCreator can interact with it's data, close the related XIB, etc.
I believe I'm getting the delegate and protocol established correctly, and verified that my 'delegate' object still contains data when it is called, but I'm getting a EXC_BAD_ACCESS method when my view controller tries to pass data back to the delegate in this line of code:
[self.delegate childViewControllerDidFinish:self];
Here's a good portion of the rest of my code. I had this working by using CITRootViewController as my delegate instead of the CITReportCreator class, but now that I'm separating the code, something has broke.
CITReootViewController.m (the view controller that calls the Report Creator)
//create a nrew report
-(IBAction)createReport:(id)sender {
CITReportCreator *report = [CITReportCreator alloc];
[report createNewReport:self];
}
CITReportCreator.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "CITImageCaptureViewController.h"
#interface CITReportCreator : NSObject <CITImageCaptureDelegate>
#property (strong, nonatomic) NSArray *imageList;
#property (nonatomic) NSInteger imageIndex;
-(int) createNewReport:(UIViewController *)parent ;
//Delegate Methods
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
And CITReportCreator.m
#import "CITReportCreator.h"
#implementation CITReportCreator
{
UIViewController *parentController;
}
#synthesize imageList;
#synthesize imageIndex;
-(int) createNewReport:(UIViewController *)parent
{
//store a reference to the parent view controller
parentController = parent;
// init code....
//head to the first image capture view
[self startImageCapture];
return 0;
}
-(int)startImageCapture
{
//pull the image name from the array of images
NSString *imageName = [imageList objectAtIndex:imageIndex];
//prep the image capture controller
CITImageCaptureViewController *capture = [[CITImageCaptureViewController alloc] initWithNibName:#"CITImageCaptureViewController" bundle:nil];
//Assign the capture controller's delegate
capture.imageName = imageName;
capture.delegate = self;
//Display the capture controller
[parentController presentModalViewController:capture animated:YES];
return 0;
}
//a break point set here never gets hit.
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
{
[viewController dismissModalViewControllerAnimated:YES];
}
#end
And finally, the CITImageCaptureViewControllers
CITImageCaptureViewController.h
#import <UIKit/UIKit.h>
#protocol CITImageCaptureDelegate <NSObject>
-(void) childViewControllerDidFinish:(UIViewController*)viewController;
#end
#interface CITImageCaptureViewController : UIViewController
{
id<CITImageCaptureDelegate> delegate;
}
#property (nonatomic,assign) id<CITImageCaptureDelegate> delegate;
#property (nonatomic, assign) NSString *imageName;
//continue button pressed method
-(IBAction)continueButtonPressed:(id)sender;
#end
And the .m file
#import "CITImageCaptureViewController.h"
#interface CITImageCaptureViewController ()
#end
#implementation CITImageCaptureViewController
#synthesize navItem;
#synthesize imageName;
#synthesize delegate = _delegate; //i think this may be part of the problem
//cutting out initWithNibName, viewDidLoad, etc...
- (IBAction)continueButtonPressed:(id)sender
{
[self.delegate childViewControllerDidFinish:self];
}
#end
I find nothing with delegates and protocols all that simple, but I'm guessing I'm missing a small change somewhere. Can you help me head in the right direction?
I am passing an NSDictionary object from one view class to another as I transition from a table view to a normal view to show details:
Passing Controller:
[tweetController setTweet:tweet];
Receiving Controller.h:
#interface TweetViewController : UIViewController {
NSDictionary *tweet;
}
#property (nonatomic, retain) NSDictionary *tweet;
Receiving Controller.m:
#implementation TweetViewController
#synthesize tweet = _tweet;
I then try to use this information to set the properties of some fields in my view:
- (void)viewDidLoad
{
[super viewDidLoad];
tweetLabel.text = [_tweet objectForKey:#"text"];
}
The result is a blank label and if I inspect the value of _tweet at this stage it is nil.
I originally had a method which set the value of tweet which I called at the same location as I am now setting the value. If I inspected the value at this stage it was fine.
I presume that the automagic setter through #synthasize is working, but somewhere else the value is being lost.
Sorry this is my first objective C anything! Thanks for any help in advance.
You are using your "tweet" instance variable, whereas the "tweet" property is synthesized to the "_tweet" variable.
You are probably calling the setTweet method after viewDidLoad executes.
I usually pass this kind of thing into a custom init method.
Alternatively, you could do the set before pushing the detail VC onto the nav stack.
Are you sure that tweetLabel isn't nil?
I've made a few corrections & optimisations to your code. You don't need to declare ivars in the header file anymore, they are generated automatically by #synthesize
- (void)dealloc; is only needed if you're not using ARC.
//.h
#interface TweetViewController : UIViewController
#property (strong, nonatomic) NSDictionary *tweet;
#property (strong, nonatomic) IBOutlet UILabel *tweetLabel
#end
//.m
#implementation TweetViewController
#synthesize tweet = _tweet;
#synthesize tweetLabel = _tweetLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.tweetLabel.text = [self.tweet objectForKey:#"text"];
}
- (void)dealloc {
[_tweet release];
[_tweetLabel release];
[super dealloc];
}
#end
Note: strong is equivalent to retain
To expand on #Rayfleck's answer, since you are new to Objective-C, your custom init method could look like this:
In TweetViewController.h:
- (id)initWithTweet:(NSDictionary*)tweet;
In TweetViewController.m:
- (id)initWithTweet:(NSDictionary*)tweet
{
self = [super init];
if (self) {
_tweet = tweet;
}
return self;
}
and then in your passing controller you'd allocate and initialize like this:
TweetViewController *tvc = [[TweetViewController alloc] initWithTweet:myTweet];
I have created a tab based application having 4 tabs and 4 views respective to these tabs.
I have a string in first view and when I printing this string in second view it printing null.
In first view.h
NSString *dateString;
#property(nonatomic,retain) NSString *dateString;
In first view.m
#synthesize dateString;
dateString=button6.titleLabel.text;
NSLog(#"dateString:%#",dateString);
In second view.h
NSString *dateString;
#property(nonatomic,retain) NSString *dateString;
In second view.m
#synthesize dateString;
- (void)viewDidLoad { [super viewDidLoad];
NSLog(#"dateString:%#",self.dateString);
}
Add your view controllers as properties for the application delegate (if the app is a relatively simple design).
Then you can reference the properties of the second view controller from the first view controller, by way of the app delegate. (One such property could be the string you want the second VC to copy or retain.)
Create NSString variable in Application delegate class and set the Property and make synthesize that variable.
And set the #"" (blank) value in applicationDidFinishLaunching method.
For Example - my variable name is str, then initialize str in applicationDidFinishLaunching like self.str = [NSString stringWithFormat:#""];
And now you can use it in any tab *view* and set the value as per your require.
More code
AppDelegate.h
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
NSString *baseURL;
}
#property (nonatomic, retain) NSString *baseURL;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window;
#synthesize baseURL;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
self.baseURL = [NSString stringWithFormat:#""];
}
- (void)dealloc
{
[baseURL release];
[window release];
[super dealloc];
}
#end
ViewController1.h
#class AppDelegate;
#interface ViewController1 : UIViewController <UITextFieldDelegate>
{
AppDelegate *appDelegate;
}
#end
ViewController1.m
#import "AppDelegate.h"
#import "ViewController1.h"
#implementation ViewController1
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSLog(#"value - %#",appDelegate.baseURL); // Here you can set or get the value.
}
it may not be the best answer.but creating a string variable in the appdelgate and passing the variable to this from the first view and fetching it from the second view works for me
Really, did we lose focus of MVC and the most awesome of abilities that is easy to do in iPhone Development?
How about a delegate?
#protocol ViewOneDelegate
- (void)getStringVariable;
#end
#interface ViewOneModel : NSObject
{
NSString* _stringVariable;
id<ViewOneDelegate> _theDelegate;
}
#property (nonatomic, retain) id<ViewOneDelegate> theDelegate;
#end
Assign a controller to be the delegate for the ViewOneModel.
Here is a simple solution, but not the best one, Create a global variable, and just use that.
Header
extern NSString *GlobalString;
#interface GlobalVariables : NSObject {
}
#end
implementation
#import "GlobalVariables.h"
#implementation GlobalVariables
NSString *GlobalString;
#end
And now to have access to the variable just import the header in the file you want to use.
You'll probably want to check if it's initiated before you use it.
I'm my class i've added a an instance of my view controller, created a property and then synthesized it in my implementation file. I am trying to update the UIlabel in the view controller like this,
NSString *currentChar = [[NSString alloc] initWithFormat:#"%c", ch];
viewController.outputLabel.text = currentChar;
[currentChar release];
My problem is that everything builds without any errors or warnings but the label just doesn't get updated, what am I doing wrong. I'd really appreciate some help on this one.
Are you sure you're referencing the existing viewController and you didn't instantiate a new one? Your property is not declared as copy, correct?
textProcessor.h / .m
#interface textProcessor : NSObject {
MainViewController *mainView;
}
#property (retain) MainViewController *mainView;
#end
#implementation textProcessor;
#synthesize mainView;
MainViewController.h / .m
#interface MainViewController : UIViewController {
UILabel *myLabel;
}
#property (retain) UILabel myLabel;
#end
#implementation MainViewController
#synthesize myLabel;
When you are initializing your textProcessor class, and you set the value for "mainView" like
-(void)viewDidLoad {
[super viewDidLoad];
textProcessor *proc = [[textProcessor alloc] init];
proc.mainView = self;
//note that you are not doing this:
//MainViewController *mainView = [[MainViewController alloc] init];
//proc.mainView = mainView;
//that was creating a new instance variable instead of using self, the existing one
[textProcessor release];
}
Have you created your label in IB? If you are using IB you have to create an IBOutlet for your UILabel. You then make a connection between the UILabel in IB to your IBOutlet in your class.
Have you tried calling the setNeedsDisplay method on the view? Also you may want to try using the setText method instead of assigning directly to the property.
My application is a tab bar application, with a separate view controller for each tab.
I have an object in my first view controller (A) which contains all my stored application data (Please ignore NSUserDefaults for this) which needs to be accessed by the second view controller (B) when I press a button on it. How can I achieve this in the best way?
One option you have is to declare your date model as instance variables of your app delegate (as mentioned by other commenters).
Instead of referencing the app delegate as suggested by nevan an alternative is to add a property to your view controller classes (A and B) for your data model.
Say you wanted to share a data model object between your view controllers you can add a property to each:
#interface AViewController : UIViewController {
MyDataModel *model;
}
#property (nonatomic, retain) MyDataModel *model;
#end
#interface BViewController : UIViewController {
MyDataModel *model;
}
#property (nonatomic, retain) MyDataModel *model;
#end
When you initialise your view controller you can then set this property to the object context initialised previously.
You have mentioned a tab bar controller. If your view controllers are wired through IB all you have to do is to set these parameters in your application delegate applicationDidFinishLaunching: method, before the tab bar controller is displayed:
#interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{
MyDataModel *model;
AViewController *aViewController;
BViewController *bViewController;
...
}
#property (retain) IBOutlet AViewController *aViewController;
#property (retain) IBOutlet BViewController *aViewController;
#end
#implementation MyAppDelegate
...
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
...
aViewController.model = model;
bViewController.model = model;
[window addSubview:tabBarController.view];
[window makeKeyAndVisible];
}
Don't forget to release the model in your view controller's dealloc method.
The alternative is to use a singleton object. An simple singleton example:
#interface MyDataModel : NSObject
{
}
+ (MyDataModel *) sharedDataModel;
#end
#implementation MyDataModel
static MyDataModel *sharedDataModel = nil;
+ (MyDataModel *) sharedDataModel
{
#synchronized(self)
{
if (sharedDataModel == nil)
{
sharedDataModel = [[MyDataModel alloc] init];
}
}
return sharedDataModel;
}
#end
You can access this data model from all your view controllers with something similar to the following:
MyDataModel *model = [MyDataModel sharedDataModel];
See also this stack overflow discussion about singletons.
The most common way I've seen this is to set up the thing you want to access in the app delegate and reference it in other places like this:
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
myStuff = appDelegate.stuff;
In the app delegate, set up a stuff variable and use #property and #synthesize as usual.
Some people say that it's not a good approach, since it's the same as using global variables, but it's very common.
I like to create a top level Model class that is a singleton and contains all the elements I might need.
It's helpful to also give it a top level load method that populates objects with just the db keys, using the hydrate/dehydrate pattern common in the Apple examples.
Typical usage in the app delegate would be simply,
[[MyModel sharedModel] load];
And then in a view controller:
NSArray *myThing1s = [[MyModel sharedModel] thing1s];
NSArray *myThing2s = [[MyModel sharedModel] thing2s];
You can then iterate over your thing1s and thing2s and when you need details, you can just call
[myThing1 hydrate];
which will populate the object.
Of course, you probably want to use CoreData to manage the persistence from 3.0 onwards.
I always create a special object called DataModel and use it's singleton sharedInstance.
And this object then holds all the app-related-data. No need for accessing the dreaded appDelegate.
DataModel.h
#import <Foundation/Foundation.h>
#class MyClass1, MyClass2;
#interface DataModel : NSObject
#property (copy, nonatomic) NSString *aString;
#property (assign) BOOL aBool;
#property (strong) MyClass1 *myObject1;
#property (strong) MyClass2 *myObject2;
+ (DataModel *)sharedModel;
#end
DataModel.m
#import "DataModel.h"
#import "Class1.h"
#import "Class2.h"
#implementation DataModel
- (id) init
{
self = [super init];
if (self)
{
_myObject1 = [[MyClass1 alloc] init];
_myObject2 = [[MyClass2 alloc] init];
aBool = NO;
aString = nil;
}
return self;
}
+ (DataModel *)sharedModel
{
static DataModel *_sharedModel = nil;
static dispatch_once_t onceSecurePredicate;
dispatch_once(&onceSecurePredicate,^
{
_sharedModel = [[self alloc] init];
});
return _sharedModel;
}
#end
And (bacause I'm lazy) i put DataModel.h in application-prefix.pch.
That way i can access my data from anywhere in the application simply by calling
[DataModel sharedModel]
Both view controllers should reference a third object (C) as their dataSource; this object (C) containing all the stored application data.
C would be, in this case, the M in the MVC.
Add to each of your ViewControllers the following declarations:
// SomeViewController.h
// Before #interface
#class MyDataSource;
// In the interface
IBOutlet MyDataSource *datasource;
#property(retain) IBOutlet MyDataSource *datasource;