Reputation: 16292
I seem to have forgotten the basics regarding this or perhaps I am just too tired.
I have a view controller and a view that is a subclass of UIView
that is named MyViewClass.
In viewDidLoad
of my viewController I alloc
and init
self.myView
like this:
self.myView = [[MyViewClass alloc] initWithFrame:(CGRect){{0, 0}, 320, 480}];
self.view = self.myView;
At some time in my viewController, I would call [self.view setNeedsDisplay];
. No problem here.
I expose a property in MyViewClass.h:
@property(nonatomic) i;
The property i
will be used in drawRect
in myView;
So, from my viewController I would set it like so:
self.myView.i = 100;
So my question is:
Why am I able to call [self.view setNeedsDisplay];
in my view controller without needing to call [self.myView setNeedsDisplay];
and drawRect
will be called in myView, but I can't just do self.view.i
in my view controller as self.myView
has been assigned to self.view
already in viewDidLoad
in my view controller?
Upvotes: 0
Views: 4429
Reputation: 5655
You are able to call [self.view setNeedsDisplay]
, but not self.view.i
because UIViewController
's view
property is of type UIView
.
self.myView has been assigned to self.view already in viewDidLoad
Remember that the actual assignment happens at runtime, while compiler errors are generated at compile time. The compiler has no idea in what order your view controller's methods may be called, so it can't make any guesses about the actual type of a property's value beyond the property's original declaration. As far as Xcode is concerned, self.view
is just a UIView
.
As danh suggested above, you can override the property declaration to inform the compiler that your view is of type MyView
. Although, your approach of using the self.myView
property may be preferred anyway: it allows you to change up the view hierarchy in the future without changing your interface, and doesn't muck with inherited methods. UITableViewController
does the same thing: both the view
and tableView
properties return the same view instance, but the properties are typed differently.
It's important to remember that the type of a property is not necessarily the same as the type of its value. self.view
may have been assigned to an instance of MyView
, but the property is still of type UIView
. Regardless of the type of object assigned to it, the property's accessor is still a method with return type UIView
.
So, as far as the compiler is concerned, self.view
is nothing more than a plain old UIView
, even if you know that it isn't.
I think it would help to elaborate more on what properties are, and differentiate between the property and the instance. When you create a property like this:
@interface MyViewController : UIViewController
@property(strong, nonatomic) MyView *myView;
@end
The compiler translates it into a variable, an accessor method (the "getter"), and a mutator method (the "setter"). The property is merely shorthand for declaring the methods like so:
@interface MyViewController : UIViewController
{
MyView *_myView;
}
- (MyView *)myView; // accessor / getter
- (void)setMyView:(MyView *)myView; // mutator / setter
@end
@implementation MyViewController
- (MyView *)myView
{
return _myView;
}
- (void)setMyView:(MyView *)myView
{
_myView = myView;
}
@end
When you call self.myView
, or self.view
, you are actually calling the accessor method; it's equivalent to [self myView]
or [self view]
. What it returns is a pointer to an object in memory. Because you assigned self.view = self.myView
, both properties are set to the same object; thus, both self.view
and self.myView
return a pointer to the same object.
To summarize:
self.view = self.myView
generates no compiler error, because MyView
is a subclass of UIView
. Note that assigning self.myView = self.view
would generate a warning because UIView
is not a subclass of MyView
[self.view setNeedsDisplay]
causes myView
to draw itself, because the same instance is assigned to both properties. If you log the descriptions (NSLog(@"view=%@, myView=%@", self.view, self.myView)
) for both properties, you can observe that they have the same memory addressself.view.i
because the view
property is declared to have type UIView
, and UIView
has no method named i
.Upvotes: 2
Reputation: 62686
You're trying to substitute the VCs view with a UIView subclass, have it work in all the usual ways, but also give the vc's code access to properties on the subclass? I think you can get there by overriding the view getter:
- (MyView *)view {
return (MyView *)[super view];
}
Also, you can setup the custom view in IB as the VC's view, so there's no need to do anything in loadView.
Upvotes: 0
Reputation: 535118
In viewDidLoad of my viewController I alloc and init self.myView like this:
self.myView = [[MyViewClass alloc] initWithFrame:(CGRect){{0, 0}, 320, 480}];
self.view = self.myView;
Never never never do that in viewDidLoad
. viewDidLoad
means you already have a view. You should never ever change your view controller's view.
It is fine (and required) to set your view loadView
. That is what it is for. But in viewDidLoad
, absolutely not.
Upvotes: 1