Search code examples
iosuiviewautolayoutdrawinglayoutsubviews

How does the view redraw itself when I disable/enable a button?


If I enable/disable a button…what method would get called on the view?

I’ve already placed a breakpoint on the layoutSubviews and don’t see it reaching there. Nor it reaches us to the viewController's viewWillLayoutSubviews. So I'm just curious to know how it works and what method it triggers


Solution

  • That's a good question. I got curious about this myself, so I created a simple UIButton subclass and put breakpoints on a few overridden methods. Here are my findings:

    1. Called setEnabled: on a UIButton subclass instance which I called MyButton in viewDidLoad
    2. This triggered a call to setNeedsDisplay method. It was called internally by the setEnabled method of the UIButton class. As you can see from the stack trace in this screenshot:

      enter image description here

    3. After setNeedsDisplay, the setNeedsLayout method was triggered. The stack trace was similar to the earlier call:

      enter image description here

    4. After this the following methods were called in this order: layoutSubviews, drawLayer:inContext:, drawRect:

    According to Apple's documentation of the UIView class,

    When the actual content of your view changes, it is your responsibility to notify the system that your view needs to be redrawn. You do this by calling your view’s setNeedsDisplay() or setNeedsDisplay(_:) method of the view. These methods let the system know that it should update the view during the next drawing cycle. Because it waits until the next drawing cycle to update the view, you can call these methods on multiple views to update them at the same time.

    And here is the documentation on the enabled boolean of UIControl:

    A Boolean value indicating whether the control is enabled. Set the value of this property to YES to enable the control or NO to disable it. An enabled control is capable of responding to user interactions, whereas a disabled control ignores touch events and may draw itself differently. Setting this property to NO adds the UIControlStateDisabled flag to the control’s state bitmask; enabling the control again removes that flag.

    They mention that a disabled control may re-draw itself differently. They also mention the control's state bitmask is updated. So, I believe, internally Apple's UIButton class calls the setNeedsDisplay method, which in-turn forces to re-draw itself. Based on the (UIControlState) state property, the button draws itself respectively. You can find more on UIButton drawing here (look at Table 2 Appearance attributes) and UIView drawing here.

    But it probably won't trigger any events on the UIViewController's view which has the button as a subview. Although, you could use KVO and observe the enabled property if you want to listen to changes to the property in your view controller or in the button's superview.