4

I have read some articles that what's the difference between setNeedsLayout and layoutIfNeeded, while what I am focusing is:

1. do I need to call these two method together if I want layout immediately, because I saw this kind of combination so many times

2. when do I need to call setNeedsLayout? upon my understanding, If I change view's frame, it will update layout during next cycle, that I don't have to call setNeedsLayout explictly

whitekevin
  • 311
  • 3
  • 15
  • Possible duplicate of [setNeedsLayout and setNeedsDisplay](https://stackoverflow.com/questions/14506968/setneedslayout-and-setneedsdisplay) – Au Ris May 18 '18 at 07:57

2 Answers2

8

How these things work is through invalidation to remove redundancy. A view will contain information if needs to layout or not.

So calling setNeedsLayout will just set some internal boolean value needsLayout to true. Once layoutIfNeeded is called it will check this boolean value

if needsLayout { 
    needsLayout = false
    doMagic() // Calls layoutSubviews at some point
}

Why this is designed this way is because multiple calls may invalidate layout but we want to layout it only once or as fewer times as possible.

In most cases you will not need to call setNeedsLayout because most changes already do that for you. For instance you may change a constraint value and invalidation is done for you. All you need is to call layoutIfNeeded and your views will update. To be more correct you don't even need to call layoutIfNeeded as it will do that for you in the next cycle. But you will need to call it if you want the change animated for instance and you need to do that in animation block.

myViewConstraint.constant = 40.0 // Will already call setNeedsLayout
UIView.animate(withDuration: 0.3, animations: {
    myView.layoutIfNeeded()
})

So changing constraints does nothing but change the information on how the view(s) should be layout. Only the call to layoutIfNeeded will actually use those values and change layout. That is why you only need to put that in the animation block (it is not wrong to put it all in the block though).

To be fair there have been some changes where now by default (you may disable it) animate methods already layout your views by themselves so you can do with even less code but that is not the point at the moment.

So:

  1. You do not need to call the 2 methods together to layout immediately. If the view layout is already invalidated (which is in most cases) then layoutIfNeeded is enough. But note that setNeedsLayout is as trivial as setting a boolean internally to true so there is no harm in calling it, just a precaution. So calling both is safer. Calling setNeedsLayout alone will do nothing "immediately" though.

  2. Hopefully you never need to call setNeedsLayout. There are some complex situations where you need to explicitly invalidate layout and there are a few possible UI bugs. In all other cases this will be done for your. But note that if you come to a situation where you need to call this "it will update layout during next cycle" will not be true. Until the view layout is invalidated it will not layout at all.

I am not sure where setNeedDisplay fits in your question (it is only in your title) but this one works the same way but is a bit more complicated. It will invalidate its content and force it to redraw, call drawRect. This must occur during its drawing pipeline, not just anytime so you may not explicitly call it to redraw. And if you do nothing will happen (maybe crash) since it will have no context to draw on. If you override drawRect and you resize your view it will try to cache your drawn content and use contentMode to resize the drawing. By default it is set to scaleToFill which means your content will be stretched as view size changes. You will need to call setNeedDisplay in order for your drawRect to be called again and you may redraw content accordingly.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43
  • Hi, @Matic, nice answer. One more question about "_In most cases you will not need to call setNeedsLayout because most changes already do that for you. For instance you may change a constraint value and invalidation is done for you_". Could you share where this can be found (in docs, maybe)? – pacification May 18 '18 at 08:54
  • @pacification As I said above answer, FYI – whitekevin May 18 '18 at 09:02
  • @pacification I have never seen a doc describing this specifically. It might be berried somewhere in the docs but most likely it is just mentioned somewhere. The thing is that this logic may change internally over time and had been doing that over multiple versions of OS. The point is if all goes well you never call invalidation BUT if at some point you see an anomaly it might be what fixes it. Note that the whole UI layout and rendering is extremely complicated thing. Mostly due to optimizations. So things can easily go wrong internally. – Matic Oblak May 18 '18 at 10:10
2

I found what I want to ask, when should I call setNeedLayout, when should not. since the aim to call setNeedLayout is to call layoutSubviews, so for situations as follows, I don't have to do call setNeedLayout

  • Resizing a view
  • Adding a subview User
  • scrolling a UIScrollView
  • (layoutSubviews is called on the UIScrollView and its superview)
  • User rotating their device
  • Updating a view’s constraints
whitekevin
  • 311
  • 3
  • 15