6
votes

I am looking to find out how to correctly subclass UIView with a custom init method passing in parameters.

I have read the UIView class reference documentation here: https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html

At present the UIView is being created in the UIViewController and can be moved/refactored into its own subclass. Also at present the view is created completely in code and the frame is calculated by the constraints added to the view itself.

The documentation says the following

initWithFrame: - It is recommended that you implement this method. You can also implement custom initialization methods in addition to, or instead of, this method.

Question
As I do not create a frame as a starting point for the view / neither is it loaded from a XIB what is the correct process to subclass this?

Would this be correct:

-(id)init {
    NSLog(@"%s",__PRETTY_FUNCTION__);
    self = [super init];
    if (self) {
        // Init code
        [self spmCustomInit];
    }
    return self;
}

-(void)spmCustomInit {
    NSLog(@"%s",__PRETTY_FUNCTION__);

}

If this is is correct I need to further change this. When the view is being created it creates some subviews based on information. Also the layout is different based on a difficulty level. Further question
How do I create a further custom init method which I pass in parameters?
For example if I created a method called

spmInitWithWord:(NSString *)word difficulty:(GameTurnDifficulty)difficulty

How is the standard init method then called. Would I then call this custom init when creating the view initially too? [[UICustomViewExample alloc] spmInitWithWord:testWord difficulty:turnDifficulty]??

2

2 Answers

5
votes

No, I would suggest a different approach. Write your init method to call super initWithFrame. That's the designated initializer for UIView. By calling it yourself, you assure that any OS setup that needs to take place gets invoked.

You can pass in CGRectZero as the frame if you need to, and then set the frame later.

Note that you should also plan to support initialization using initWithCoder. That's the method that gets called if you put your view into an XIB file or Storyboard.

What I do is to create a method doInitSetup, and put my custom initialization there. I then call that method from both initWithFrame and initWithCoder.

In your case, you could add properties for your custom settings, and if you put one of your views in an XIB/Storyboard, you could set those properties using user runtime attributes.

Using IB to design your forms is a good idea. I would counsel you to learn to use them. They make like easier and give you access to features that are all but impossible to use from code.

Your code might look like this:

- (id) initWithWord: (NSString *) theWord
{
   self = [super initWithFrame: CGRectZero];
   if (!self)
     return nil;
   [self doInitSetupWithWord: theWord];
   return self;
}

- (void) doInitSetupWithWord: (NSString *) theWord
{
  //Do whatever you need to do to set up your view.
}


- (id) initWithCoder:(NSCoder *)aDecoder
{
  self = [super initWithCoder: aDecoder];
  if (!self) {
    return nil;
  }

  [self doInitSetupWithWord: nil];
  return self;
}

- (void) setWord: (NSString *) theWord
{
  _theWord = theWord;
  //If possible, set things up for the new word
}
0
votes

Let's say your custom method is as follows

-(instancetype) initWithFrame:(CGRect)frame base:(UIColor *)base textColor:(UIColor *)textColor
{
    if(self = [super initWithFrame:frame])
    {
        self.backgroundColor = base;
        self.label.textColor = textColor;
    }
    return self;
}

The if condition creates your UIView object, which you further customise depending on your parameters. You can either use the initWithFrame: or init method, whichever you prefer or works for your use case.