LayoutIfNeeded() vs SetNeedsLayout()
I’ve just deceived by these two methods. They made me rewrote an entirely single view to find bug caused by misuse between the two. So, if you are one who want to find explanation by example, this article may helps you.
The Case
I was working on an animation to works based on tableView.contentOffset
to animate another view’s height constraint. Let’s call that view headerView
. That actually pretty simple.
If you are working with ReactiveX, in my case I was using RxCocoa, then you just observe tableView’s contentOffset change to calculate headerView’s height and apply any change inside UIView.animate
function. Simple!
But if you are using default Apple’s provided tools, you may use KVO or any observation pattern you like to react to every single contentOffset
changes.
But the task isn’t finished here.
Let’s put above situation aside for a moment.
The Other Consideration
Pagination mustn’t be something strange to you. In case you are new to this word, it is something that make application not to be overburdened by fetching millions data at once. Let’s say you feed a baby spoon by spoon, not a bowl of porridge at once. Silly? Yes.
This app will receive sequence of data page by page. That requires implementation which the app should know where and when to trigger data fetching before user stuck in the middle of smooth scrolling. You usually use calculation which the simplest must be follows :
PSEUDOCODE:IF table view scrolling almost end THEN;
trigger API request
ELSE;
keep scroll
ENDIF;
You have triggered request and data is now on your hand to be displayed. You then update the backing-store (do you call your data store like this?) and notify tableView to update its cells accordingly.
Real Problem
Here the problem with my implementation occurs. I found that on each data update, tableView will update its contentOffset
in the middle of operation. That causes tableView jumped randomly to certain offset and smooth scrolling is something that hard to achieve.
My implementation included invocation on tableView.layoutIfNeeded()
in each contentOffset
update to observe and react its change to determine headerView
height state. That problem also causes headerView
height to jump randomly causing entire scroll stopped. “What a weird bug!” I thought.
Eventually, the explanation is pretty simple though it may wrong. So, feel free to correct.
When the contentOffset
changes on tableView update data, then because of layoutIfNeeded()
invoked, it performs all view update which in this case tableView scroll jumped back to certain offset
. That layoutIfNeeded()
force view to apply any change in layout without wait to natural update layout cycle (officially it called RunLoop
). The solution is to notify view to perform layout but on next cycle. That operation is achieved by setNeedsLayout()
.
That’s all. Feel free to write your thought. And correction.