29

I have a custom UIViewController and custom UIView. I'd like to override the viewcontroller.view property to return MyCustomUIView.

Right now I have:

@interface MyViewController : UIViewController {    
    IBOutlet MyView* view;
}

@property (nonatomic, retain) IBOutlet MyView* view;

This compiles but I get a warning: property 'view' type does not match super class 'UIViewController' property type.

How do I alleviate this warning?

Keith Fitzgerald
  • 5,651
  • 14
  • 43
  • 58
  • 2
    I think you should rather use `@dynamic`. Please read [this question](http://stackoverflow.com/questions/1160498/synthesize-vs-dynamic-what-are-the-differences), the answers there were really helpful to me :) – Ondrej Rafaj Oct 20 '10 at 14:41
  • 3
    There is very nice article called [Overriding UIViewController's View Property, Done Right](http://travisjeffery.com/b/2012/12/overriding-uiviewcontrollers-view-property-done-right/). – lambdas Jan 23 '13 at 08:26

5 Answers5

23

@lpaul7 already posted a link to Travis Jeffery's blog as a comment, but it's so much more correct than all the other answers that it really needs the code to be an answer:

ViewController.h:

@interface ViewController : UIViewController

@property (strong, nonatomic) UIScrollView *view;

@end

ViewController.m:

@implementation ViewController

@dynamic view;

- (void)loadView {
  self.view = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
}

@end

If you using a xib, instead of overriding loadView change the type of your view controller's root view and add IBOutlet to the property.

See Overriding UIViewController's View Property, Done Right for more details.

Max MacLeod
  • 26,115
  • 13
  • 104
  • 132
JosephH
  • 37,173
  • 19
  • 130
  • 154
  • Using that approach I get the following compiler warning: "property type 'MyScrollView *' is incompatible with type 'UIView *' inherited from 'UIViewController'" – koen Nov 03 '13 at 15:55
  • @Koen Is your MyScrollView a subclass of UIView? Do you get the compiler warning if you use UIScrollView instead of MyScrollView? – JosephH Nov 03 '13 at 16:34
  • MyScrollView is a subclass of `UIScrollView`. I ended up making the scrollView just a container for MyCustomView where I do all the drawing. As opposed to doing all the drawing the MyScrollView. – koen Nov 03 '13 at 17:35
  • I spy with my little eyes and UIScrollView w/ a retain count of 1 not being dealloc :X - corrected me if i'm wrong. – Lifely Apr 08 '14 at 15:36
  • @Lifely If you're not using ARC, you're spot on :-) If you are using ARC the code is correct (and an you'd get a compiler error if you did put in the autorelease). Alternate answer: make sure you don't get any compiler or analysis warnings and you should be good! – JosephH Apr 09 '14 at 09:31
  • @JosephH Yeah not using ARC here for some reason. I realised it later last night. I sometimes tends to forgot it's mostly used everywhere else. But yeah for arc this is correct and w/out arc there would be no warning and a autorelease would be welcome. – Lifely Apr 09 '14 at 14:57
  • @JosephH - why are not calling `[super loadView]` in your `loadView` function ? – Sabir Ali Apr 13 '16 at 10:43
  • @SabirAli Apple have historically said that you shouldn't (e.g. see http://stackoverflow.com/questions/15111153/what-are-the-side-effects-of-calling-super-loadview ). From a quick glance the current iOS documentation doesn't say that, but I presume it is still true - there is nothing [super loadView] could useful do as it's purpose is to setup self.view, and our loadView is doing that. – JosephH Apr 13 '16 at 12:31
18

The short answer is that you don't. The reason is that properties are really just methods, and if you attempt to change the return type, you get this:

  • (UIView *)view;
  • (MyView *)view;

Objective-C does not allow return type covariance.

What you can do is add a new property "myView", and make it simply typecast the "view" property. This will alleviate typecasts throughout your code. Just assign your view subclass to the view property, and everything should work fine.

Ecton
  • 10,702
  • 2
  • 35
  • 44
4

Sorry, but your short answer is wrong.

The correct implementation of this would be to add a @property to the header file with your return type. Then instead of @synthesize you add the getter and setter manually and return a cast type from [super view]

for instance my class is a PlayerViewController

PlayerViewController.h

@property (strong, nonatomic) IBOutlet PlayerView *view;

PlayerViewController.m

- (PlayerView *)view{
    return (PlayerView*)[super view];
}
- (void)setView:(PlayerView *)view{
    [super setView:view];
}

the important thing is to put the correct class into the view.

Putting a UIView where a PlayerView goes would likely work in the Interface Builder, but would not function correctly in the code.

I am currently using this implementation.

The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
  • i know this question was old. But I found this and then came up with my own answer. Hope this help the future users. – The Lazy Coder Apr 25 '12 at 22:36
  • Do you think the property should be strong? I mean, `UIView` already have strong property `view` that holds the same value. Isn't readonly modifier is more appropriate? – lambdas Jan 23 '13 at 08:20
  • +1: I thought this was possible. Thanks for confirming it. @lpaul7 - No, `readonly` doesn't make sense because then you couldn't be able to set the `view` property- hence the meaning of `readonly`. You need to have this property as `strong` to override the superclass `view` property (which has a property declaration of `strong`). – JRG-Developer Mar 08 '13 at 04:23
  • you can use weak if you like, but it really does not matter if you make sure to set it to nil on dealloc. – The Lazy Coder Mar 08 '13 at 17:11
4

The UIViewController's view property/method will return your view automatically. I think you'll just have to cast the result to MyView (or just use the id type):

MyView *myView = (MyView*)controller.view;
id myView = controller.view;

I think the code you have posted above will cause you trouble. You probably don't want to create a view member yourself (because then the controller will be storing 2 views, and might not use the right one internally) or override the view property (because UIViewController has special handling for it.) (See this question for more info about the latter.)

Community
  • 1
  • 1
Jesse Rusak
  • 56,530
  • 12
  • 101
  • 102
0

If anyone is on Swift 2.0+, you can use protocol extensions for a reusable implementation:

// Classes conforming to this protocol...
protocol CustomViewProvider {

    typealias ViewType
}

// ... Will get customView accessor for free
extension CustomViewProvider where Self: UIViewController, Self.ViewType: UIView {

    var customView: Self.ViewType {
        return view as! Self.ViewType
    }
}


// Example:
extension HomeViewController: CustomViewProvider {
    typealias ViewType = HomeView
}

// Now, we can do something like
let customView: HomeView = HomeViewController().customView
Mazyod
  • 22,319
  • 10
  • 92
  • 157