如何让UIView.animate函数变为同步执行?
解决方案
方案1:使用DispatchSemaphore(兼容iOS低版本)
需要注意绝对不能在主线程直接阻塞,否则主线程挂起后动画根本无法执行。我们可以将动画逻辑调度到主线程执行,同时用信号量在后台线程等待动画完成:
func animateScan() { let semaphore = DispatchSemaphore(value: 0) DispatchQueue.main.async { self.topConstraint.constant = self.frame.height UIView.animate(withDuration: 2.5, animations: { self.superview?.layoutIfNeeded() }, completion: { _ in semaphore.signal() }) } // 必须在非主线程执行等待操作 semaphore.wait() }
⚠️ 调用示例(需放到后台线程):
@IBAction func buttonTapped(_ sender: UIButton) { DispatchQueue.global().async { self.animateScan() // 动画完成后切回主线程present新控制器 DispatchQueue.main.async { self.presentNewViewController() } } }
方案2:使用Async/Await(iOS 15+ 推荐)
iOS 15及以上系统提供了UIView.animate的异步原生实现,无需手动处理闭包,直接用await等待动画结束:
先将animateScan改为异步函数:
func animateScan() async { topConstraint.constant = frame.height await UIView.animate(withDuration: 2.5) { self.superview?.layoutIfNeeded() } }
按钮点击事件中用await调用:
@IBAction func buttonTapped(_ sender: UIButton) async { await animateScan() presentNewViewController() }
该方案逻辑简洁,且不会阻塞主线程,是iOS15+环境下的最优选择。
关键注意点
如果在主线程直接调用semaphore.wait(),会导致主线程挂起,UI动画依赖主线程运行,最终动画完全无法执行,程序出现假死状态,必须避免这种操作。
内容的提问来源于stack exchange,提问作者Petr Bones
相关产品推荐
相关产品推荐

