I wanted to change the collection view's contentOffset.x right after pushing VC.
So I called collectionView.setContentOffset(~)
in viewWillAppear.
But It didn't work because of auto layout cycle.
However, if I call collectionView.setContentOffset
inside DispatchQueue.main.async
block, IT WORKS!
The code is below:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
collectionView.setContentOffset(
CGPoint(x: currentFolderIndex * collectionView.bounds.width), y: 0),
animated: false
)
}
}
I figured out why it had worked when I printed the order of layout methods.
DispatchQueue.main.async
block is called after viewDidLayoutSubviews
.
Does it always work like this?
Why does it work like this?
I'm so curious!!
Using async
basically says "do it when you have time". So it will be done at an indeterminate time in the future (generally not too long after), and probably after finishing what is currently being done. And the calling function does not wait for the code to be executed on the main queue before continuing.
So in your case, when calling async
, the UI thread finishes what he is doing, calling viewWillLayoutSubViews
, doing autolayout, etc,... and when he has finished and has time, he executes the setContentOffset
asynchronously.
However, in theory it does not guarantees it will always be the same order : UI thread could find time before, or could be overbooked and not have time before viewDidAppear
... But in practical, I think you are safe assuming it will be the same order 99,99% of the time.
Note : In other situations, you could think of using DispatchQueue.main.sync
, which would say "Do it ASAP and I'm waiting for you". However, this is probably is a terrible idea, especially if you are in the UI thread : you would be waiting in the UI thread for the UI thread to do something else, and you cannot get out of that - it is a deadlock. More details here.