I made a subclass of UIView
that has a fixed frame. So, can I just override init
instead of initWithFrame:
? E.g.:
- (id)init {
if ((self = [super initWithFrame:[[UIScreen mainScreen] bounds]])) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
The Xcode documentation for -initWithFrame:
says: "If you create a view object programmatically, this method is the designated initializer for the UIView
class. Subclasses can override this method to perform any custom initialization but must call super
at the beginning of their implementation."
What does "designated initializer" mean?
The designated initializer is the one that all the other initializers must call. UIView
and subclasses are a little unusual in that they've actually got two such initializers: -initWithFrame:
and -initWithCoder:
, depending on how the view is created. You should override -initWithFrame:
if you're instantiating the view in code, and -initWithCoder:
if you're loading it from a nib. Or, you could put your code in third method and override both those initializers such that they call your third method. In fact, that's often the recommended strategy.
So, for example, you might create a UIView subclass, ClueCharacter
, that has its own initialization method: -initWithPerson:place:thing:
. You then create your view like this:
Obj-C:
ClueCharacter *mustard = [[ClueCharacter alloc] initWithPerson:@"Col. Mustard"
place:kInTheStudy
thing:kTheRope];
Swift:
var mustard = ClueCharacter("Col. Mustard", place: kInTheStudy, thing: kTheRope)
That's fine, but in order to initialize the UIView part of the object, your method must call the designated initializer:
Obj-C:
-(id)initWithPerson:(NSString*)name place:(CluePlace)place thing:(ClueWeapon)thing
{
if ((self = [super initWithFrame:CGRectMake(0, 0, 150, 200)])) {
// your init stuff here
}
}
Swift:
func init(name: String, place : CluePlace, thing : ClueWeapon)
{
if (self = super.init(CGRectMake(0, 0, 150, 200))) {
// your init stuff here
}
}
If you want to call your subclass's initializer -init
, that's okay as long as you call -initWithFrame:
in the implementation.
in UIView
calling [super init]
is exactly equal to [super initWithFrame:CGRectZero]
In an Objective-C class with multiple initialisers, the designated initialiser is the one that does the meaningful work. So, often you have a class with a few initialisers, say:
- (id)initWithRect:(CGRect)someRect;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour;
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
linkTo:(id)someOtherObject;
In that case you'd normally (but not always) say that the third was the designated initialiser, and implement the other two as e.g.
- (id)initWithRect:(CGRect)someRect
{
return [self initWithRect:someRect setDefaultColour:NO];
}
- (id)initWithRect:(CGRect)someRect setDefaultColour:(BOOL)setDefaultColour
{
return [self initWithRect:someRect setDefaultColour:setDefaultColour
linkTo:nil];
}
If a class has only one initialiser then that's the designated initialiser.
In your case to follow best practice you should implement initWithFrame:
and also a vanilla init:
that calls initWithFrame:
with your normal dimensions. The normal convention is that you can add new variations on init
in subclasses, but shouldn't take any away, and that you always do the actual initialising work in the designated initialiser. That allows any initialising methods from the parent class that you don't provide new implementations of still to work appropriately with your subclass.