How to initialize and add a subview to a custom UIView?

I have a custom view inherited from UIView. Now I want to add a UISegmentedControl as a subview to it. So the first question is: should the property of UISegmentedControl be weak or strong? (With IBOutlets I know that Apple recommends using strong since 2015). And the second question is where do I initialize it and set its layout. As I understand I shouldn't do this in the drawRect: method. Should it be initialized in initWithFrame: method, added as a subview to my custom view and then its layout to be set in layoutSubviews like so:

- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];

if (self) {
    NSArray *options = @[@"option1", @"option2", @"option3"];
    self.segmentedControl = [[UISegmentedControl alloc] initWithItems:options];
   [self.segmentedControl addTarget:self action:@selector(someAction:) forControlEvents:UIControlEventValueChanged];

    [self addSubview:self.segmentedControl];
}
return self;
}

- (void)layoutSubviews {
[super layoutSubviews];

CGRect segmentedControlFrame = CGRectMake(self.bounds.size.width / 4.0, 50, self.bounds.size.width / 2.0, 30);
self.segmentedControl.frame = segmentedControlFrame;
self.segmentedControl.tintColor = [UIColor blackColor];
[self.segmentedControl setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateSelected];
}

or just do all this in layoutSubviews: method:

- (void)layoutSubviews {
NSArray *options = @[@"option1", @"option2", @"option3"];
self.segmentedControl = [[UISegmentedControl alloc] initWithItems:options];

CGRect segmentedControlFrame = CGRectMake(self.bounds.size.width / 4.0, 50, self.bounds.size.width / 2.0, 30);
segmentedControl.frame = segmentedControlFrame;
segmentedControl.tintColor = [UIColor blackColor];
[segmentedControl setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateSelected];
[segmentedControl addTarget:self action:@selector(someAction:) forControlEvents:UIControlEventValueChanged];

[self addSubview:segmentedControl];
}

1 answer

  • answered 2017-08-12 09:45 macmoonshine

    You should use strong if your view will keep the segmented control if it is not contained in it's hierarchy. You can use weak when the segmented control is always part of the hierarchy, but you have to keep it with a strong reference until you add it to the hierarchy. In your first code snippet you must use a local (strong) variable, which holds the control:

    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
    
        if (self) {
            NSArray *options = @[@"option1", @"option2", @"option3"];
            UISegmentedControl *control = [[UISegmentedControl alloc] initWithItems:options];
    
            [control addTarget:self action:@selector(someAction:) forControlEvents:UIControlEventValueChanged];
    
            [self addSubview:control]; // By now the view keeps the control
            self.segmentedControl = control;
        }
        return self;
    }
    

    The advantage of a weak reference is that it is automatically resetted, if you remove the control from the hierarchy. This may help you to keep the amount memory small.

    For the layout you should prefer autolayout contraints. Autosizing is possible, too (take a look at translatesAutoresizingMaskInotConstraints anyway). layoutSubviews shouldn't add the segment to the view, because it is called several times.