Discrepancy in passing data between classes - iphone

Part 1 of the code works fine. Part 2 shows minor changes in the code which causes the code to stop working (without errors/warnings) as expected.
Part 1: (Works)
#import "ClassA.h"
#import "ClassB.h"
#implementation ClassA
- (void) sendData
{
NSString *temp = [NSString stringWithFormat:#"HI!"];
ClassB *classBObject = [[ClassB alloc] init];
classBObject.dataToDisplay = temp;
self.view = classBObject.view;
}
#end
Interface of ClassB:
#import <UIKit/UIKit.h>
#interface ClassB : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *textLabel;
#property NSString * dataToDisplay;
#end
Implementation of ClassB:
#import "ClassB.h"
#implementation ClassB
#synthesize dataToDisplay, textLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
textLabel.text = dataToDisplay;
}
#end
Part 2:
But if I change - (void)sendData of ClassA to the following:
- (void) sendData
{
NSString *temp = [NSString stringWithFormat:#"HI!"];
ClassB *classBObject = [[ClassB alloc] init];
classBObject.textLabel.text = temp; // statement changed from Part 1.
self.view = classBObject.view;
}
and remove textLabel.text = dataToDisplay; from implementation of ClassB, the textLabel on view controller of ClassB does not get updated. Can you please suggest, why is it so?
Thanks!
Edit 1:
In the statement: classBObject.textLabel.text = temp; // statement changed from Part 1., I had missed .text while copy pasting. Please excuse me for that.

The reason that the second technique is incorrect (besides the missing .text at the end of textLabel) is that when you return from the class B initializer, the underlying UILabel corresponding to textLabel undoubtedly has not been created yet. If you look at this diagram you'll see that the view configuration is not completed at the end of the the initialization methods, but rather upon access. So you must defer any access of the user interface controls until viewDidLoad.
Update:
When I run the following code, I get "0x0" in my log, proving that the UILabel on my second view is still nil and has not been initialized yet, as I would have expected. When the viewDidLoad in my second controller sets self.textLabel.text = self.dataToDisplay in viewDidLoad, it works like a champ (as it does for you). But the UILabel IBOutlet property is just not reliable until viewDidLoad.
SecondViewController *controller = [[SecondViewController alloc] init];
NSLog(#"%p", controller.textLabel);
controller.dataToDisplay = #"from first view";
[self presentViewController:controller animated:YES completion:nil];

You are setting a UITextLabel to NSString. Try
classBObject.textLabel.text = temp;

Change this line...
classBObject.textLabel = temp; // statement changed from Part 1.
to
classBObject.textLabel.text = temp; // statement changed from Part 1.
Also, you should do
[self.view addSubView:classBObject.view]; //using navigation controller or presenting modal viewcontroller would be recommended.
instead of
self.view = classBObject.view;
after this line, update the label's text with your value.
classBObject.textLabel.text = temp; // statement changed from Part 1.

I noticed that you're using a weak reference to the UILabel in your classB interface. Any reason you're not using a strong reference? The only time you want to use weak references is to avoid retain cycles. Most likely, your UILabel isn't being retained.
Where do you initialize your UILabel?

Related

How to access values from a different UIViewController

How can I access the value from an inputField located in a second viewController?
The class name of the second view controller is SettingsViewController and the outlet name for the inputField is setRateInput.
I tried this but it didn't work…
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
when I NSLog it comes out as The value is: (null)
Any idea what I'm doing wrong?
Here is the implementation file for the main viewController:
#import "SettingsViewController.h"
#interface ViewController ()
#property (strong, nonatomic) SettingsViewController * settings;
#end
#implementation ViewController
// lazy instantiation
-( SettingsViewController *) settings
{
if (_settings == nil) {
_settings = [[SettingsViewController alloc]init];
}
return _settings;
}
- (IBAction)calculatePrice:(id)sender {
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
#end
In theory, you could create a global. Create a new class, call it something like taxRate (.h and .m)
In taxRate.h, add the following code:
#import <Foundation/Foundation.h>
#class MyTaxRate;
#interface TaxRate : NSObject {
}
#property (nonatomic, retain) double * taxRateFromInput;
+(TaxRate*)getInstance;
#end
Then, in your controller, put a "#import taxRate.h" in there. In your .m file, add the following:
#import "TaxRate.h"
#implementation TaxRate
#synthesize taxRateFromInput;
static TaxRate *instance =nil;
+(TaxRate *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [TaxRate new];
}
}
return instance;
}
#end
Note: This is extremely similar in structure to what I'm purposing.
if you have the reference from the object view controller you can just access by the property from your attribute.
You instantiated a new SettingsViewController, but you didn't do anything to instantiate its textfield setRateInput. You can do it when you instantiate it:
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
or, as a beter solution, instantiate the text field in -init of SettingsViewController
- init {
if (self = [super init] {
self.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
}
return self;
}
If you use nib files, this would be a lot easier.
Note: setRateInput is a bad name for a property. Consider rateTextField instead.
Edit I forgot to add that you have to add the text field as a subview to its parent view.
So it will be like,
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[_settings.view addSubView:_settings.setRateInput];
In this case, the setRateInput is retained by its super view. You're not using ARC, so you can call autorelease on your text field.
The better solution: Use - (void) loadView; inside SettingsViewController. Loading the view is the responsibility of the correspondent view controller.
- (void) loadView {
self.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[self.view addSubView:_settings.setRateInput];
}
Edit: xib files and storyboards can help you out. Give these tutorials a try.
You are on the right track, also well done with your lazy instantiation (as
a demonstration that you grasped the concept, I mean).
But note, that outlets don't get connected until viewDidLoad is called. So if you
just alloc/init your viewController (lazily), the outlet to your textfield is pointing to nil.
The outlet doesnt get connected until your controller's view property is accessed, ie the view is displayed.
What you could do is give the settings viewController a handle to your calculating viewController and let it set a public property on the calculating viewController that represents the rate.
This is a common pattern - delegation - where one viewController (settingsViewcontroller) calls a method on its delegate (calculating viewController).
You wouldn't need the settingsViewcontroller property in your calculating viewController then, but just instantiate a new settings viewController every time you want it to be brought up, giving it a reference to your calculating viewController.
Another possibility - maybe even better - is to define a model object that does calculation and takes care of the rate it needs to calculate. Then you could give your settingsViewcontroller a reference to that model object (probably instantiated in your
other viewController), so that it can change the rate on it.
PS: also re think how you instantiate viewControllers generally. The designated initialiser is -initWithNibName:bundle: - so usually, you wouldn't just alloc/ -init them.
If you use storyboards (you probably should!), use storyboard's -instantiateViewControllerWithIdentifier: or use the above mentioned designated initialiser.

NSDictionary setting to nil when passed to another class (IOS)

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];

Accessing variables from other viewcontroller [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Interact with a view controller from another view controller
viewcontrollerOne.h
UILabel *label;
viewcontrollerTwo.m
-(void)myMethod
{
viewControllerOne *obj = [[viewControllerOne alloc]init];
obj.label.text = #"abcd";
}
I want to access variables of one viewcontroller in other view controller.I tried the above method but it's not working for me.i searched other threads but most of the answers told me to declare them in appdelegate. so is there any other way instead of declaring it in appdelegate?
create a new method in viewcontrollerTwo (don't forget to declare it in *.h file):
-(id) setParams:(UILabel *)lb{
labelOne = [[UILabel alloc] init];
labelOne = lb
return self;
}
Then, when you need to puth this view controller from first one:
viewcontrollerTwo *controllerNew = [[viewcontrollerTwo alloc] initWithNibName:#"viewcontrollerTwo" bundle:nil];
[self.navigationController pushViewController:controllerNew animated:YES];
controllerNew = [controllerNew setParams:labelOne];
[controllerNew release];
You code is wrong, you should have labelOne, not label:
obj.labelOne.text = #"abcd";
Also, your label may not exist yet - do you create it in the init function? If you have a xib file for that controller then you need to load that so that all the controls are created and linked up:
viewcontrollerOne *controllerNew = [[viewcontrollerOne alloc] initWithNibName:#"viewcontrollerOne" bundle:nil];
Also, it would be much nicer to declare your label as a property rather than a member variable:
#property (strong, nonatomic) UILabel *labelOne
Then synthesize the setter/getter methods in the implementation file:
#synthesize labelOne;
Finally, Cocoa naming standards are that class names should start with a capital letter.
See its very easy... you can do it through App Delegate class.
Write the Variables which you want in appDelegate class like in .h
UILabel *myLabel;
#property (nonatomic) UILabel *myLabel;
IN .m File :-
synthsize myLabel;
myLabel = [[UILabel alloc]init];
Now in Whichever viewController u need to get this Label do this way :-
in .h file :-
#class AppDelegateClass; //forward decleration
AppDelegateClass *appDelegate;
UILabel *localLabel; //CurrentViewController Label
//Make Property
in .m File :-
//synthesize the Label
appDelegate = (AppDelegateClass *)[[UIApplication SharedApplication]delegate];
//now to acccess it
localLabel = appDelegate.myLabel;
and Finished...
Hope it helps and if then Please Tick as Correct Answer and UpVote the Answer.... :)
Please try below code instead of your code. and it will work fine...
viewControllerOne *obj = [viewControllerOne alloc];
obj.variableName = #"abcd";
[obj initWithNibName:#"nibName"];
Thanks

How can I update my view controller's UILabel through another class?

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.

calling a function in view controller of Utility app

I am new to objective C and I have a c++ background. I want to display a value in the label on the screen. I am calling the label value from the MainView.m. However, the label becomes blank after I click a button instead of printing a value. What is the problem? Here is the code.
MainView.h
#interface MainView : UIView {
int a;
}
-(int) vr;
#end
MainView.m
-(int) vr
{
return 100;
}
#end
MainViewController.h
#interface MainViewController : UIViewController {
IBOutlet UILabel *myLabel;
NSMutableString *displayString;
MainView *view1;
}
#property (nonatomic, retain) UILabel *myLabel;
#property (nonatomic, retain) NSMutableString *displayString;
(IBAction)showInfo;
(IBAction) pressButton:(id) sender;
#end
MainViewController.m
#synthesize myLabel, displayString;
-(IBAction) pressButton:(id) sender{
[displayString appendFormat:#"%i", view1.vr];
myLabel.text = displayString;}
- (void)viewDidLoad {
view1 = [[MainView alloc] init];
[super viewDidLoad];}
- (void)dealloc {
[view1 dealloc];
[super dealloc];}
I have not mentioned code that had been auto generated. This is enough to get the whole picture. I tried a lot to debug this thing. I believe that IBAction carries out direct command such that
myLabel.text = #"string";
but it does not invoke any method or class. Any subtle ideas? Thanks.
Few issues:
1
In MainView.h you declare -(id) vr;
And in MainView.m it returns int.
2
Maybe pressButton is not connected to the right event in Interface Builder (it is usually touch up inside).
Try to write to log in this method.
3
Maybe myLabel is not connected to the label in the Interface Builder.
Try to set tome hard-coded string to label's text property.
4
Do you initiate view1 in some place?
Can you post this piece of code too?
5
You can use [displayString appendFormat:#"%i", view1.vr];...
EDIT (due to changes in question):
6
The line [super viewDidLoad]; should be the first line inside viewDidLoad.
7
[view1 dealloc]; - never call dealloc directly on objects. Call release instead. The only place, where you can and should use dealloc is the line [super dealloc]; inside dealloc method.
8
When you format your question/answer in Stack Overflow, remember that each code line should start with at least 4 spaces (or tab). Try reformatting you question by adding 4 spaces in the beginning of each code line.
9
I think that displayString is not initiated. Add the next line in the viewDidLoad: displayString = [NSMutableString new];