2

UIButton can be configured to use different styling, title, etc when the button is enabled or disabled, e.g. with UIButton.setTitle(String, forState:UIControlState).

ReactiveCocoa lets me hook up a ReactiveSwift.Action to the button's reactive.pressed property, and if the Action is disabled the button will show disabled styling: this is great!

But a ReactiveSwift.Action is also disabled while it has a SignalProducer in progress. This locking is useful for UI elements attached to slow actions (e.g. network requests) but produces undesirable visual flicker when the action is quick-but-not-instant.

A simple workaround is to not use the builtin UIButton disabled styling: instead we can expose a Property<Bool> somewhere and use it both to enable/disable the Action and to explicitly change the button's styling. This is somewhat clumsy and awkward, however, so my question is: is there a "clean" way to combine the UIButton builtin disabled styling with disabling the button via explicitly disabling the Action inside UIButton.reactive.pressed, without showing the disabled style when the action is in progress?

Tikitu
  • 679
  • 6
  • 22

1 Answers1

2

We ended up using the application reserved area in the UIControlState bitset to explicitly encode this:

extension UIControlState {
    public static var myActionIsExecuting = UIControlState(rawValue: 1 << 16)
}

public class ReactiveAwareStylingButton: UIButton {
    override public var state: UIControlState {
        return [super.state, customState]
    }
    var customState: UIControlState {
        if reactive.pressed?.isExecuting.value == true {
            return [.myActionIsExecuting]
        }
        return []
    }
}

That way we can use the standard state-dependent styling options, e.g.

button.setTitleColor(normalColor, for: .normal)
button.setTitleColor(normalColor, for: [.disabled, .myActionIsExecuting])
button.setTitleColor(disabledColor, for: .disabled)

We also still get the option, if desired, to use the disabled styling while the action is executing (this might be appropriate for slow actions e.g. network requests), simply by ignoring the .myActionIsExecuting state.

If applying this technique do note that the compiler will cheerfully allow you to make a semantic error: you can apply styling as above to a UIButton instance that is not the custom subclass ReactiveAwareStylingButton, which will never enter the state .myActionIsExecuting. You should protect against this error, e.g. by applying the styling only in a function taking a ReactiveAwareStylingButton parameter.

Tikitu
  • 679
  • 6
  • 22