setNeedsLayout vs layoutIfNeeded Explained

Let’s take a look at the difference between setNeedsLayout and layoutIfNeeded.

As part of the normal startup process, UIApplication in iOS starts the main run loop for an app, which runs on the main thread. The main run loop processes events (such as user touches) and handles updates to view-based interfaces. As events occur, such as touch, location updates, motion, and multimedia control, the run loop finds the appropriate handler for the events, calling appropriate methods, which call other methods, and so on. At some moment in time, all events will have been handled and control will return to the run loop. Let’s label this point where control is returned to the run loop as the update cycle, since this is how Apple refers to it in some of their documentation. You could use other terminology to conceptualize it, such as a break in the action, the redraw cycle, or a free moment.
While events are being processed and dispatched, and as changes to views are requested, they are not always acted upon right away. Instead, the system records the changes and marks views as needing to be redrawn. When are the changes drawn? It is in this update cycle, after all existing events have been handled, that attention is now turned to redrawing. Of course to the user it does not look like there is a wait to redraw (in general), because all of this is happening quickly. Knowing that there is an interval periodically, between sets of events being processed, where the system now takes on the task of updating the layout and display, is important for understanding setNeedsLayout and layoutIfNeeded.
The method setNeedsLayout for a UIView tells the system that you want it to layout and redraw that view and all of its subviews, when it is time for the update cycle. This is an asynchronous activity, because the method completes and returns immediately, but it isn’t until some later time that the layout and redraw actually happens, and you don’t know when that update cycle will be.
In contrast, the method layoutIfNeeded is a synchronous call that tells the system you want a layout and redraw of a view and its subviews, and you want it done immediately without waiting for the update cycle. When the call to this method is complete, the layout has already been adjusted and drawn based on all changes that had been noted prior to the method call.
So, stated succinctly, layoutIfNeeded says update immediately please, whereas setNeedsLayout says please update but you can wait until the next update cycle.
When I first encountered these methods, I can remember thinking that the if needed part of layoutIfNeeded made it sound less urgent or even optional, compared to the setNeedsLayout method that sounded more like a definitive statement to perform a layout. However, names can be a little deceiving.
setNeedsLayout is an easy one: it just sets a flag somewhere in the UIView that marks it as needing layout. That will force layoutSubviews to be called on the view before the next redraw happens. Note that in many cases you don't need to call this explicitly, because of the autoresizesSubviews property. If that's set (which it is by default) then any change to a view's frame will cause the view to lay out its subviews.
layoutSubviews is the method in which you do all the interesting stuff. It's the equivalent of drawRect for layout, if you will. A trivial example might be:
-(void)layoutSubviews {
    // Child's frame is always equal to our bounds inset by 8px
    self.subview1.frame = CGRectInset(self.bounds, 8.0, 8.0);
    // It seems likely that this is incorrect:
    // [self.subview1 layoutSubviews];
    // ... and this is correct:
    [self.subview1 setNeedsLayout];
    // but I don't claim to know definitively.
}
AFAIK layoutIfNeeded isn't generally meant to be overridden in your subclass. It's a method that you're meant to call when you want a view to be laid out right now. Apple's implementation might look something like this:
-(void)layoutIfNeeded {
    if (self._needsLayout) {
        UIView *sv = self.superview;
        if (sv._needsLayout) {
            [sv layoutIfNeeded];
        } else {
            [self layoutSubviews];
        }
    }
}
You would call layoutIfNeeded on a view to force it (and its superviews as necessary) to be laid out immediately.

Comments

Popular posts from this blog

iOS Architecture

Property vs Instance Variable (iVar) in Objective-c [Small Concept but Great Understanding..]