iPhone - properties, ivars, etc - iphone

On this tutorial, the author has these declarations:
on .h
UIViewController *presentingViewController;
...
#property (retain) UIViewController *presentingViewController;
on .m
#synthesize presentingViewController;
at some point in code, inside a block, he does:
self.presentingViewController = viewController;
and then
[presentingViewController dismissModalViewControllerAnimated:NO];
I find this very strange. If is is assigning the viewController to self.presentingViewController, should't it be calling
[self.presentingViewController dismissModalViewControllerAnimated:NO];
?
I have changed his code to
.h
#property (retain) UIViewController *presentingViewController;
.m
#synthesize presentingViewController = _presentingViewController;
and what I do is:
self.presentingViewController = viewController;
and then
[self.presentingViewController dismissModalViewControllerAnimated:NO];
the problem is that, self.presentingViewController is nil at this line, even being declared as retain and never being released.
any clues?
thanks

Did you remove the instance variable UIViewController *presentingViewController when you added the instance variable UIViewController *_presentingViewController ? If not i would put money on you accidentally using the wrong one at some point.
Stepping through.. originally you had an instance variable
UIViewController *presentingViewController;
And from the property syntactic sugar you have the two accessor methods for setting and getting the variable
- (UIViewController *)presentingViewController;
- (void)setPresentingViewController:(UIViewController *)val;
at some point in code, inside a block, he does:
[self setPresentingViewController: viewController]; // uses setter
and then
[presentingViewController dismissModalViewControllerAnimated:NO]; // uses instance variable
which is fine, but you think it should be
UIViewController *localPresentingViewController = [self presentingViewController]; // uses getter
[localPresentingViewController dismissModalViewControllerAnimated:NO];
which is also fine but somewhat unnecessary.
Then you added a new instance variable:-
#synthesize presentingViewController = _presentingViewController;
so now you have
UIViewController *presentingViewController;
UIViewController *_presentingViewController;
// These now get / set _presentingViewController
- (UIViewController *)presentingViewController;
- (void)setPresentingViewController:(UIViewController *)val;
But almost certainly, somewhere you are confusing the two ivars (or you aren't aware you have two ivars) presentingViewController / _presentingViewController and still have a reference to the presentingViewController ivar, leading you to think your property is nil.

You are right that it should be:
[self.presentingViewController dismissModalViewControllerAnimated:NO];
However, reinstate this:
#synthesize presentingViewController;
And remove the instance decoration of a similar looking variable:
UIViewController *presentingViewController;

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.

How Can I Access my ViewController's Property Variables From Another Class?

Ok, this is really bugging me and I am sure the solution is simple... I am unable to set my ViewController's property variables from another class (SeverConnect.m), which I have declared and synthesized properly in my ViewController's .h/.m files:
ServerConnect.h:
#import <Foundation/Foundation.h>
#import "ViewController.h"
#import "Contact.h"
#class ViewController;
#interface ServerConnect : NSObject
{
Contact *newContact;
NSString *codeRawContent;
NSMutableArray *contactListCopy;
... //Other variables declared here, but not shown in order to save space
Inside ServerConnect.m:
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(#"parserDidFinish");
newContact = [[Contact alloc] initWithCodeInfo:(NSString *)codeInfo
contactName:(NSString *)completeName
contactImage:(UIImage *)profileImage
contactWebSite:(NSString *)codeRawContent];
[contactListCopy insertObject:newContact atIndex:0];
[ViewController setContactList:contactListCopy]; //Automatic Reference Counting Error Occurs Here: "No known class method for selector 'setContactList:'"
}
As I mentioned above, I have declared and synthesized the property variable, "contactList", in my ViewController's .h/.m files (with no errors):
#property (nonatomic, retain) NSMutableArray *contactList; //In ViewController's .h file
#synthesize contactList; //In ViewController's .m file
Can someone tell me what I am doing wrong? Thanks in advance for any help you can provide!
you are trying to access an instance property on a class:
[ViewController setContactList:contactListCopy];
you need to first create an instance of the ViewController class, and then set its property. Something like this:
ViewController *viewController = [[ViewController alloc] init];
[viewController setContactList:contactListCopy];
In this line of code:
[ViewController setContactList:contactListCopy];
you should be using a variable of type ViewController. The way you are using it, it should be a class method, not a property.
Write something like:
ViewController *viewController = [[ViewController alloc] init];
[viewController setContactList:contactListCopy];

Assigning RootViewController to UIViewController gives warning - why?

I want to access the RootViewController of my App in one of its classes in order to present a modal view controller. I do this by getting the ApplicationDelegate and asking it for the RootViewController and store it in a UIViewController
AppDelegate *appDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;
UIViewController* presentingViewController = appDelegate.viewController;
In my opinion this should work without a warning as RootViewController inherits from UIViewController. However I receive this warning:
Incompatible pointer types initializing 'UIViewController *__strong' with an expression of type 'RootViewController *'
Can someone explain to me why I see this warning?
If it helps - this is the AppDelegate where I define the RootViewController:
#interface AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
RootViewController *viewController;
}
#property (strong) RootViewController *viewController;
I defined my RootViewController like this:
#interface RootViewController : UIViewController {
}
You can assign an object to a variable declared as its superclass. That is no problem and is very useful when you only want to use superclass methods over a set of your own subclasses, especially common with view controllers in a navigation stack when the specific type of next view controller is unknown.
Also think about it. Methods like
[self presentModalViewController:customController animated:YES];
wouldn't work without being able to do this. This method is declared as taking a UIViewController * but you pass in a custom UIViewController subclass with no complaints. Finally
[rootViewController isKindOfClass:[UIViewController class]];
will return YES. QED.
Have you forward declared your class RootViewController in the header for your app delegate?
i.e.
#class RootViewController;
#interface AppDelegate : NSObject <UIApplicationDelegate> {
....
Did you spell it correctly? This is a common area to mistype as xCode doesn't autocomplete forward declarations. It will then autocomplete your typo in the rest of the header file.
Did you remember to import the header file for your RootViewController into the .m file for the AppDelegate? You will still need to do that so the compiler knows about the inheretance.
Your code looks correct at the moment but we don't have all of it.
The problem is that RootViewController is not the same class as UIViewController.
In the AppDelegate, you declare viewController to be of type RootViewController. Then, in these lines:
AppDelegate *appDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;
UIViewController* presentingViewController = appDelegate.viewController;
You are creating presentingViewController, which is of type UIViewController, and setting it to an instance of RootViewController. This is the source of the error.
Fix this by using a consistent type.
Read What's the difference between the RootViewController, AppDelegate and the View Controller classes that I may create? for a nice explanation of the difference between these two types.

iphone: Implement delegate in class

I am trying to call up a modal table view controller using presentModalViewController but I am not sure what to do about the delegate. The following code gives me an error:
MyRidesListView *controller = [[MyRidesListView alloc] init];
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
[controller release];
Error:
Request for member 'delegate' is something not a structure or union
Now, I realized there is no delegate property in my MyRidesListView class. So, how would I add a reference to my delegate there? What am I missing here?
Generally delegates are properties defined as such:
id<NameOfDelegateProtocol> delegate;
And:
#property (nonatomic, assign) id<NameOfDelegateProtocol> delegate;
EDIT: You said your parent class is UITableViewController. You may have wanted to do this:
controller.tableView.delegate = self;
Why do you think you need a delegate? Why not just remove the "controller.delegate = self" line. Otherwise you need to implement a delegate system the way I outline below or else make MyRidesListView a subclass of a viewcontroller that implements delegates.
It looks like you cut and pasted some sample code that uses a delegate, then substituted your own viewcontroller that doesn't provide a delegate. If you don't know what the delegate is for, then just delete that line.
I'll cut and paste some actual code from one of my test programs to show you how it's done:
from the Interface file:
Add a delegate instance variable to your class and make it a property so you can use the "blah.delegate = foo" syntax to set it.
#interface BAPClient : NSObject {
CGSize imageSize;
id <BAPClientDelegate> delegate;
}
#property (nonatomic, readonly) CGSize imageSize;
#property (nonatomic, assign) id <BAPClientDelegate> delegate;
#end
// define the protocol spoken. (what the delegate must implement)
#protocol BAPClientDelegate
- (void)addTile:(BAPTile *)tile;
#end
in the implementation, you must call the delegate at the appropriate time:
- (void)deliverTile:(BAPTile *) tile {
NSLog(#"%s tile=%p",__FUNCTION__,tile);
if ([self delegate])
[[self delegate] addTile:tile];
[tile release];
}
Try to set the delegate object by the setter
[controller setDelegate:self];
This often works wonders.

Objective-C - simple setting property example erroring with 'request for ___ in something not a structure or union'

I've been banging my head with this one this evening and am sure it's something very simple that I've missed
I've created a new project with the appdelegate and a view controller class as below. The view controller synthesises the property in the .m, and the app delegate header imports the view controller .h file. Code below:
View controller header:
#interface untitled : UIViewController {
NSString *string;
}
#property (nonatomic, retain) NSString *string;
App delegate:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
testViewController = [[untitled alloc] initWithNibName:#"untitled" bundle:nil];
testViewController.string = #"Testing String";
[window addSubview:testViewController.view];
[window makeKeyAndVisible];
}
Can someone please help and point out the obvious mistake as to why setting the string property fails here with the error mentioned? Is it because of being inside this method? I've never had issues setting properties in other methods before after initing a view controller.
Thanks.
The error is saying it does not understand that the class has that property. It means you have either the wrong class or that it knows nothing about the class.
So, you need to add:
#import "untitled.h"
in your application delegate - also, you need to have the variable be of type "untitled" (I am pretty sure you declared the type as UIViewController and not untitled):
untitled * testViewController = (untitled *)[[untitled alloc] initWithNibName:#"untitled" bundle:nil];
By the way, by convention you should always start class names in uppercase.