In UIView layout, what is an "update cycle"? - uiview

According to the documentation for -[UIView setNeedsLayout]:
Because this method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated. This behavior allows you to consolidate all of your layout updates to one update cycle, which is usually better for performance.
Sounds great - but when I use setNeedsLayout without then calling layoutIfNeeded, I find that my control doesn't lay itself out. I had hoped that an "update cycle" would happen before the control was next shown, but I guess that isn't how it works. So what is an "update cycle"? When does one happen?

The 'update cycle' happens at the end of the current run loop cycle.
setNeedsLayout must be called on the main thread (main runloop).

That's strange, I use a lot of custom drawn stuff that I change and call "[self setNeedsLayout]" a lot and I never had to call "layoutIfNeeded"...
Are you sure your "drawRect" is fine and not having some problem? Maybe its data isn't ready before you call "setNeedsLayout".
The first answer to the following thread could help you further.

I don't think there is such an evident bug in UIKit. Being you, I would check if my code calls all base methods I overrided. Especially, in hierarchy parents of the problematic object. If this doesn't help, I would replace custom parent view with some standard views to see if the problem persists. This would help to isolate the problem.

Use layoutIfNeeded instead, which triggers immediate layout update.
setNeedsLayout just schedules layout to the handler, being executed until the next update cycle.
refer to: http://www.iosinsight.com/setneedslayout-vs-layoutifneeded-explained

Related

Calling layoutIfNeeded() right after setNeedsLayout()

I had a conversation with my colleagues about setNeedsLayout() and layoutIfNeeded().
Starting from the conclusion, my colleague says
setNeedsLayout() 'may' or 'should' be called before layoutIfNeeded(), because layoutIfNeeded() may not conduct layout if we don't set the 'layout flag' to true by calling setNeedsLayout().
Whereas, my thought is, calling both of them in the same place will lead to the same result as we call layoutIfNeeded() only. Because...
What I know about them is, as described here and here,
setNeedsLayout() is just invalidating the current layout and then the coming update cycle would take care of the rest.
layoutIfNeeed() is updating the layout immediately, so don't want to the coming update cycle.
Simply saying, async call and sync call. And that's it.
If there is no animation, I don't recall I've seen a lot of cases I had to use layoutIfNeeded(Perhaps one or two). Most of the cases, it worked quite well only with setNeedsLayout(). Moreover, if there is a case that needs to call both of them in the same place, I would rather say it's a bug from UIKit.
Can someone explain me which one is correct, and why?
First, a little discussion what these methods do, the answer to your question is below that.
From the documentation of setNeedsLayout():
This method makes a note of the request and returns immediately. Because this method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated.
Usually, you need to call this if there's some external factor affecting the layout that UIKit doesn't know about (e.g. old fashioned manual layouting code by overriding layoutSubviews()). It doesn't trigger an immediate re-layout, that usually only happens on the next main runloop iteration. This allows you to mark several views as needing layouts, you can call it on the same view multiple times within the same runloop iteration, but UIKit only does the actual layouting once (as I just noted, usually on the next runloop iteration).
But if you need the layout to get settled right now (e.g. because you need to measure its result), you need to call layoutIfNeeded().
If no layout updates are pending, this method exits without modifying the layout or calling any layout-related callbacks.
This is a crucial point about layoutIfNeeded(): if the layout system doesn't know there are pending changes, it simply won't do any. Calling setNeedsLayout() is the way we tell the layout system that there are changes that require a layout run.
If you only use auto-layout and manipulate just the constraints of the view you are about to re-layout, you can skip setNeedsLayout() since that has already been called indirectly; but setNeedsLayout() is very cheap, so there's no harm in calling it if you're unsure.
So, after calling setNeedsLayout(), do you need to call layoutIfNeeded()? To simply update the view on screen, no, you do not need to call layoutIfNeeded() since UIKit will do so on its own on the next main runloop iteration. In fact, you might negatively impact render performance if you always call layoutIfNeeded() even though your code doesn't need the updated layout right away since this can result in multiple layouting runs within the same runloop iteration.
But if you rely on the layout information immediately (for example, you need to measure the height of a view), you do need to call it. Just beware to have your design not do this too often.
Your comparison with "async" and "sync" is indeed somewhat fitting: in a way, you can see setNeedsLayout() as triggering an asynchronous operation and layoutIfNeeded() "waits" until it has happened (that's not what's actually going on, but from a certain point of view that is the observable effect).

The occasion to call setNeedsLayout or setNeedDisplay

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
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:
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.
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.
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

Autolayout constraints update with size classes

I know, how to create autolayout constraints with size classes perfectly.
But I am not getting when to call layOutIfNeeded(), setNeedsDisplay(), layOutSubViews(), setUpdateConstraints().
Can someone tell how to properly call this function to update UI after constraints changed.
Another my concern is, when to call only single function out of above and call with other functions.
It must be really clear that your layout is calculated by a routine that is called at specific times at runtime.
It could happen that you need to modify the current layout, for instance changing the constant of a specific constraint. If you just do that you will notice no changes in the UI, this is because the routine is still not called.
What you can do is force the layout routine to be called, and you do that by these two methods:
setNeedsLayout : You are telling that the view needs a layout. The next time the routine is called knows that this view need to have a layout refresh
layOutIfNeeded(): You don't want to wait the next call and you are telling the system to force layout calculation ASAP
Same thing happen with setNeedsDisplay() and displayIfNeeded(), with the first you tell that a view needs to be rendered again, and with the second you tell do ASAP.
If you are asking yourself why, the reason is performance. Is useless to re-render everything each time, this lazy approach will save system resources.
The methods - setNeedsUpdateConstraints and -updateConstraintsIfNeeded are basically the same concept applied to constraints, the difference is that you will not see any changes in UI until you force a layout, why this methods are useful? because sometimes you need to check after a change in constraint if the layout is still valid without changing the aspect of your UI.

Does willMoveToSuperview will also deallocate the UIView on which its got called?

I was wondering if I can call willMoveToSuperview on UIView and after that retain that view to reuse later for one ? something like following
if (!CGRectIntersectsRect(cell.frame, visibleRegion)) {
[cell willMoveToSuperview:nil];
[self.resuableCells addObject:cell];
}
I am not sure about your intent here...
But WillMoveToSuperview - According to doc:
The default implementation of this method does nothing. Subclasses can override it to perform additional actions whenever the superview changes.
So your code,
[cell willMoveToSuperview:nil];
Has no effect unless you override this method in a cell subclass and implement your own logic there.
Coming to your question -
Does willMoveToSuperview will also deallocate the UIView on which its got called?
Answer is obvious - NO.
willMoveToSuperview is an observer method that the system calls as a courtesy to you in order to give you a chance to handle special cases before it completes some other hidden tasks.
It's default behavior is to do nothing, but you might want to tidy up something in your code prior to a move by overriding this method.
A proper use case might be if you had a view playing a video clip or an animation, and something else in your code is about to rip the view out of it's current hierarchy and place it in some other un-related view hierarchy. You might want the chance to pause the clip or suspend the animation before the move took place.
I doubt it's the right method to handle what you are attempting, and I definitely know you should not be calling it directly.
Feel free to post some more code to show us what you're trying to accomplish and where it's going wrong.

How is layoutIfNeeded used?

When and how is layoutIfNeeded used? I know that when we change the layout of a view, we can call setNeedsLayout to update the layout but not sure when layoutIfNeeded should be used.
NOTE: I have layoutIfNeeded used in actual code but forgot in what context it was used.
layoutIfNeeded forces the receiver to layout its subviews immediately if required.
Suppose you have overridden layoutSubviews, and UIKit feels that your view requires layout for whatever reason (e.g. you called setNeedsLayout when handling some user action). Then, your custom layoutSubviews method will be called immediately instead of when it would normally be called in the regular UIKit run loop event sequence (after event handling, but before drawRect:).
An example of why you might need to call layoutIfNeeded within a single run loop:
You resize a custom view containing a table view with a custom layout. setNeedsLayout is set so that layoutSubviews will be called later.
A controller object asks the table view to scroll to some particular cell when handling a user event.
Your custom view performs some custom sizing of the table view in layoutSubviews that changes the table view size.
The problem is when the controller asked the table view to scroll (step 2), the table view had bounds that were stale. The updated bounds would only be set on the table view later (step 3). What the controller wanted the table view to scroll to may not actually be visible after layoutSubviews is done. A solution then would be for the controller to call layoutIfNeeded in situations where it knows this might occur.
The difference between these two methods can be now be described by referencing the update cycle.
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.
LayoutSubViews() - Don’t call directly, instead call
setNeedsLayout(),override if constraint base not offer expected
behaviour.
SetNeedsLayout()- Call on main thread, it wait for next drawing cycle.
good for performance.
LayoutIfNeeded() - Layout subviews immediately.
setNeedsLayout actually calls layoutIfNeeded, so if your calling setNeedsDisplay there's no reason to call layoutIfNeeded. In this way setNeedsLayout is a convenience method for calling layoutIfNeeded which does the heavy lifting.

Resources