HiveBrain v1.2.0
Get Started
← Back to all entries
patternswiftMinor

UIButton Subclass - Overriding getters to return placeholder/default values

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
returnuibuttongetterssubclassdefaultvaluesoverridingplaceholder

Problem

I'm in the process of converting a custom UIButton from Objective-C to Swift which has custom properties and default values for those properties:

MyButton.h

@interface MyButton : UIButton

@property (nonatomic, strong) IBInspectable UIColor *buttonColor;
@property (nonatomic, strong) IBInspectable UIColor *buttonSelectedColor;

// ...

@end


MyButton.m

@implementation MyButton

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {    
        [self adjustButtonColor];
    }

    return self;
}

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

    return self;
}

- (void)setSelected:(BOOL)selected
{
    [super setSelected:selected];

    [self adjustButtonColor];
}

- (void)setHighlighted:(BOOL)highlighted
{
    [super setHighlighted:highlighted];

    [self adjustButtonColor];
}

- (void)adjustButtonColor
{
    if (self.selected || self.highlighted) {
        self.backgroundColor = [self buttonSelectedColor];
    } else {
        self.backgroundColor = [self buttonColor];
    }
}

#pragma mark - Default colors

- (UIColor *)buttonColor
{
    if (!_buttonColor) {
        _buttonColor = [UIColor grayColor];
    }
    return _buttonColor;
}

- (UIColor *)buttonSelectedColor
{
    if (!_buttonSelectedColor) {
        _buttonSelectedColor = [self.buttonColor colorWithAlphaComponent:.5];
    }
    return _buttonSelectedColor;
}

#pragma mark - Public

- (void)setButtonColor:(UIColor *)buttonColor
{
    _buttonColor = buttonColor;

    [self updateButton];
}

@end


I have attempted to replicate this pattern in Swift with public vars which return a default value, or access a private optional variable which is returned if it exists. I have also overridden the selected and highlighted properties, updating the button colors in didSet:

```
class MyButton: UIButton {

private var _buttonColor: UICo

Solution

Is this an appropriate use of private variables? Is there any way to accomplish the behavior I want with a single variable rather a second optional settable variable for each color?

This can be much easier accomplished with a lazy stored property:

lazy var buttonColor: UIColor = UIColor.grayColor()
lazy var buttonSelectedColor: UIColor =  self.buttonColor.colorWithAlphaComponent(0.5)


The right-hand side is evaluated exactly once, when the property
is accessed the first time. This makes the private _xxx variables
obsolete. (I think that you seldom need such a "shadow copy" in Swift.)

But there seems to be no real need for buttonColor to be evaluated
lazily. If you make it an ordinary stored property then you can add
a property observer:

var buttonColor = UIColor.grayColor() {
    didSet {
        self.updateButton()
    }
}


If you want buttonSelectedColor to be computed from buttonColor then
it needs to be a lazy property, otherwise you could make it a stored
property as well:

var buttonSelectedColor = UIColor.grayColor().colorWithAlphaComponent(0.5)



Is overriding UIButton properties to call adjustButtonColor in didSet abuse of the getter/setter?

That is fine. The Swift reference explicitly states: "You can also add property observers to any inherited property (whether stored or computed) by overriding the property within a subclass."


If initWithCoder is a failable initializer do I need to check if self is nil before calling adjustButtonColor()?

You don't need to (and you can't). If

super.init(coder: aDecoder)


fails then your init?(coder:) method fails immediately, and the next statement
is not reached.

Code Snippets

lazy var buttonColor: UIColor = UIColor.grayColor()
lazy var buttonSelectedColor: UIColor =  self.buttonColor.colorWithAlphaComponent(0.5)
var buttonColor = UIColor.grayColor() {
    didSet {
        self.updateButton()
    }
}
var buttonSelectedColor = UIColor.grayColor().colorWithAlphaComponent(0.5)
super.init(coder: aDecoder)

Context

StackExchange Code Review Q#131554, answer score: 3

Revisions (0)

No revisions yet.