Swift中如何在执行耗时任务前优先完成界面UI更新?
问题原因
所有UIKit的UI变更操作都不会立刻触发屏幕渲染,而是会等到当前主队列RunLoop的渲染周期到来时才会批量提交绘制。你的代码全部运行在主队列中:
- 第一行
button.setTitleColor(.clear, for: .normal)执行后,仅在内存中标记了按钮样式需要更新,没有立刻渲染到屏幕 - 第二行的
Basket.addItems是耗时操作,直接在主队列执行时会阻塞主队列RunLoop,导致之前的UI更新渲染任务被挂起,直到耗时任务执行完成后,RunLoop才会处理UI更新请求,因此你会看到UI要等耗时任务结束才变更。
解决方案
核心逻辑是UI更新保留在主队列执行,耗时任务放到后台队列执行,避免阻塞主队列的渲染流程,参考实现代码如下:
@objc private func addItemsToBasket(_ button: UIButton) { // 主队列更新UI,会在当前RunLoop周期结束后立刻渲染到屏幕 button.setTitleColor(.clear, for: .normal) // 耗时任务抛到全局后台队列执行,不阻塞主队列 DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } Basket.addItems(keys: self.planner.currentDayKeys()) // 若耗时任务结束后需要更新UI,必须切回主队列执行 DispatchQueue.main.async { // 此处放置后续UI操作,例如更新购物车角标、提示添加成功等 } } }
注意事项
- 所有UI相关操作必须放在主队列执行,不要在后台队列操作UI,否则会出现偶现崩溃、UI更新异常等问题
- 如果之前尝试GCD未生效,大概率是两种原因:一是误将UI更新代码放到了后台队列,二是耗时任务仍然留在主队列执行,没有真的放到后台队列
内容的提问来源于stack exchange,提问作者Lenny
相关产品推荐
相关产品推荐

