viewWillAppear中的DispatchQueue.main.async是否总会在viewDidLayoutSubviews后执行?
关于UIKit视图生命周期与DispatchQueue.main.async执行顺序的疑问
问题场景
推送视图控制器后,我想立即修改CollectionView的contentOffset.x,于是在viewWillAppear方法中直接调用collectionView.setContentOffset(_:),但由于自动布局周期问题,操作并未生效。
将调用放入DispatchQueue.main.async代码块后,操作成功生效,代码如下:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) DispatchQueue.main.async { collectionView.setContentOffset( CGPoint(x: currentFolderIndex * collectionView.bounds.width, y: 0), animated: false ) } }
通过打印布局方法的执行顺序,发现DispatchQueue.main.async代码块是在viewDidLayoutSubviews之后执行的,由此产生以下疑问:
疑问解答
1. 这种执行顺序是否始终成立?
在UIKit的标准视图生命周期流程下,这个执行顺序是始终稳定成立的。只要是在viewWillAppear(或更早的生命周期方法,比如viewDidLoad)中调用DispatchQueue.main.async,闭包内的代码一定会在当前视图控制器的viewDidLayoutSubviews执行完成后才运行。
2. 为什么会出现这样的执行顺序?
这要从UIKit主线程RunLoop的任务调度逻辑来解释:
- UIKit的视图生命周期方法(
viewWillAppear、viewDidLayoutSubviews等)属于主线程RunLoop当前周期内的同步任务,系统会一次性按顺序执行完这些任务。 DispatchQueue.main.async会把传入的闭包追加到主线程RunLoop的下一个任务队列中,必须等当前RunLoop周期内的所有同步任务(包括视图布局计算、生命周期方法调用)全部执行完毕后,才会处理这个异步追加的任务。- 对应到VC推送的流程:系统会依次执行
viewWillAppear→ 触发自动布局计算 →viewDidLayoutSubviews,这些都属于当前RunLoop的同步任务。你在viewWillAppear里用async添加的任务排在这些同步任务之后,自然会在viewDidLayoutSubviews执行完成后运行。此时CollectionView的布局已经完全确定,调用setContentOffset就能正确生效。
内容的提问来源于stack exchange,提问作者Nala
相关产品推荐
相关产品推荐

