views:

1102

answers:

1

Hi, I was just wondering if this approach looks like a good practice for apps with a lot of custom views, for nested PNG graphics and animations that may change based on user interaction. I created a BaseView class that extends UIView

@interface BaseView : UIView {
    @protected
    BaseViewController *controller;
}

@property (retain) BaseViewController *controller;

@end

and a corresponding controller class which is the primary location which I am putting code to manipulate the view

@interface BaseViewController : UIViewController {
    @protected
    CGRect drawArea;
}

- (void) drawArea:(CGRect) _drawArea;
- (CGRect) drawArea;
- (void) linkSubviewController: (BaseViewController *) _subviewController;

@end

where "drawArea" is the CGRect used to pass to the view as a frame.

"linkSubviewController" allows you to nest a controller and view as follows :

- (void) linkSubviewController: (BaseViewController *) _subviewController {
    [self.view addSubview:[_subviewController view]];
}

In addition I layered another custom pair called "ImageView" and "ImageViewController" which extend BaseView but also store a UIImage and an x,y,w,h

In the "drawRect" drawing methods on views I can check to see if any vars in the self.controller vars have been changed, or assign images, for example :

UIImage *image = [(ImageViewController *)self.controller image];
CGContextDrawImage( ... ) etc

I write most of the loadView methods something like this

- (void)loadView {  
    ImageView *v = [[ImageView new] initWithFrame:drawArea];
    v.controller = self;
    self.view = v;
}

The base "initWithFrame" routine contains

 self.backgroundColor = [UIColor clearColor];
 self.opaque = NO;

So I can load a variety of images with transparent backgrounds without having to assign that each time.

I've been able to use this code throughout my app and it seems to make it easy to write a top level class which assembles the layout of custom items. For animations I've been putting them in the controllers and manipulating self.view.layer.

Basically I am looking for feedback, I am new with Objective-C and the IPhone SDK

+3  A: 

There are several issues here:

  1. Using [[Classname new] init...] is incorrect usage of new. Using new is short for [[Classname alloc] init] so you are effectively calling init twice.
  2. Views shouldn't really need to know who is controlling them.
  3. Your view is retaining the controller, and since UIViewController retains its view, you have a retain cycle and neither will ever be fully released.

If you want this type of behavior (whereby a view can delegate its drawing to a parent), try creating a DrawDelegate protocol, have your controllers implement that protocol, and in your view subclass have a non-retaining drawDelegate property:

@protocol DrawDelegate
- (void) drawArea:(CGRect)rect;
@end

@interface BaseView : UIView {
    id<DrawDelegate> drawDelegate;
}
@property (assign) id<DrawDelegate> drawDelegate;
@end
Jason
Is there any specific reason to use [Class new] over [[Class alloc] init]? Other than the obvious extended initWith and whatnot.
Sneakyness
@Sneakyness +new is a convention that is slowly dying. It's inflexible compared to +alloc-init, because +new *must* call +alloc-init, whereas using +alloc means you can use any -init or -initWith... method you like.
Dave DeLong
Thanks much! The +new is an obvious mistake, thanks. I will try out the delegation approach. My main goal here is to keep stateful information in controllers and have the views re-draw based on a change in the the controller, and my app classes can run the controllers. Also, part of the plan was to be able to send touch events up to a casted .controller from custom views. Clearly needs some rework but I think delegation will help.
Michael