Rich
Rich

Reputation: 8202

How does a UIButton store the different UI states

I was looking into adding a UILabel property into a UIButton as a subview, matching the functionality of the titleLabel property currently present, and giving it all the different states of the button (UIControlStateNormal, UIControlStateHighlighted, etc.).

I started playing around with storing the states in a NSDictionary using the state (wrapped in a NSNumber) as the key to the dictionary with the values being the text, text colour and shadow colour. But I'm not sure this would work correctly based on the various different button state combinations.

So far I've got a the following methods:

-(void)setSelected:(BOOL)selected
{
    super.selected = selected;

    [self stateUpdated];
}

-(void)setHighlighted:(BOOL)highlighted
{
    super.highlighted = highlighted;

    [self stateUpdated];
}

-(void)setEnabled:(BOOL)enabled
{
    super.enabled = enabled;

    [self stateUpdated];
}

-(void)stateUpdated
{
    [self updateValueLabel];
}

-(void)setValueText:(NSString *)text forState:(UIControlState)state
{
    NSMutableDictionary *stateValues = self.customStates[@state];

    // If we haven't set the state before create a new place to store them
    if (!stateValues) {
        stateValue = [NSMutableDictionary dictionary];
        self.customStates[@state] = stateValues;
    }

    if (text) {
        stateValues[@"text"] = text;
    } else {
        [stateValues removeObjectForKey:@"text"];
    }

    [self updateValueLabel];
}

-(void)setValueTextColor:(UIColor *)textColor forState:(UIControlState)state
{
    NSMutableDictionary *stateValues = self.customStates[@state];

    // If we haven't set the state before create a new place to store them
    if (!stateValues) {
        stateValues = [NSMutableDictionary dictionary];
        self.customStates[@state] = stateValues;
    }

    if (text) {
        stateValues[@"textColor"] = textColor;
    } else {
        [stateValues removeObjectForKey:@"textColor"];
    }

    [self updateValueLabel];
}

-(void)updateValueLabel
{
    NSMutableDictionary *currentStateValues = self.customStates[@self.state];

    // Set the new values from the current state
    self.valueLabel.text = currentStateValues[@"text"];
    self.valueLabel.textColor = currentStateValues[@"textColor"];
}

I have the valueLabel as a property on the UIButton subclass.

The different attributes set for the different states don't seem to be taking effect. If I set them all manually with different bitmasks its fine, but I want to emulate the default fallback states as UIButton does for its properties.

So instead of having to set each one manually:

[button setValueText:@"Normal" forState:UIControlStateNormal];
[button setValueText:@"Normal" forState:UIControlStateHighighted];

I want to be able just to set the title once:

[button setValueText:@"Normal" forState:UIControlStateNormal];

And it show for all states.

Thanks in advance!

Upvotes: 2

Views: 452

Answers (1)

DarkDust
DarkDust

Reputation: 92335

Create a getter valueTextForState:, like Apple does with titleForState:. The documentation for this method's return value reads:

The title for the specified state. If no title has been set for the specific state, this method returns the title associated with the UIControlStateNormal state.

So, let's do this as well for your property:

- (NSString *)valueTextForState:(UIControlState)state
{
    NSString *result;
    NSDictionary *currentStateValues;

    currentStateValues = self.customStates[@(state)];
    result = currentStateValues[@"text"];

    if (!result && state != UIControlStateNormal) {
        // Fall back to normal state.
        currentStateValues = self.customStates[@(UIControlStateNormal)];
        result = currentStateValues[@"text"];
    }

    return result;
}

You would do the same for your color. Then your updateValueLabel would look like this:

-(void)updateValueLabel
{
    // Set the new values from the current state
    self.valueLabel.text = [self valueTextForState:self.state];
    self.valueLabel.textColor = [self valueColorForState:self.state];
}

Upvotes: 1

Related Questions