iOS: UIView subclass init or initWithFrame:?

69,900

Solution 1

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.

Solution 2

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.

Share:
69,900
ma11hew28
Author by

ma11hew28

Updated on June 28, 2020

Comments

  • ma11hew28
    ma11hew28 almost 4 years

    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?

  • Caleb
    Caleb over 12 years
    You're right that the designated initializer is usually the one with the most parameters, but that doesn't have to be the case. If there's more than one initializer for a class, the class documentation (or at least the header file) should say which one is the designated initializer.
  • Tommy
    Tommy over 12 years
    I actually think this may be what's causing the confusion; pointing out which is the designated initialiser in the documentation makes it sound like you're supposed to infer something deeper than you are when the class has only one or two initialisers. On the other hand, adopting the policy of always flagging the designated initialiser is obviously the correct way to document.
  • Caleb
    Caleb over 12 years
    I'm not sure I follow that... there are only two ways to know for sure which is the designated initializer: read the code, or read the docs. If the code isn't available, the docs are all you've got. It might be a good convention to make the designated initializer the one with the most parameters, but it's not currently something that you can count on in code you haven't written.
  • Tommy
    Tommy over 12 years
    Yes, I've updated my answer since I didn't mean to say that "most parameters = designated initialiser". My comment just now is that if you read the documentation for a class with a single initialiser you'll often see it described as the designated initialiser because whoever wrote it up was being prudent and thorough. However, if you don't appreciate what a designated initialiser is you're likely to become sidetracked even though you'd be in no worse a position if someone edited the word 'designated' out. I don't think they should because I'm a belt and braces man, I'm just making the point.
  • Lee Fastenau
    Lee Fastenau over 10 years
    My understanding is that only one initializer (the designated initializer) should 1) call the designated initializer of the superclass, and 2) contain the initialization logic. Any other initializers should call the designated initializer (in this case: [self initWithPerson:nil place:nil thing:nil]). Further, the designated initializer of the superclass should be overridden to call the new designated initializer.
  • Caleb
    Caleb over 10 years
    @LeeFastenau It's generally true that there's just one designated initializer, but it's not always true. As described above, a view may be initialized with either -initWithFrame: or -initWithCoder:, and neither of those calls the other. So there really are effectively two designated initializers in the case of views. See also the docs for -initWithFrame:.
  • Warpzit
    Warpzit almost 8 years
    With autolayout and constraint simply make your own init and call super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))