iOS15下等待UIView动画完成的runUntilDate替代方案咨询
iOS15 动画同步等待替代方案
核心方案(信号量实现,和原runUntilDate逻辑一致)
该方案既保留原逻辑让出主线程执行权、不阻塞线程的特性,又不需要迁移后续代码到completion回调中,适配复杂嵌套业务场景,同时兼容所有iOS版本。
// 初始化信号量,初始值为0 dispatch_semaphore_t sem = dispatch_semaphore_create(0); [UIView animateWithDuration:0.7f animations:^{ // 保留原有动画逻辑不变 } completion:^(BOOL finished) { // 动画执行完成后发送信号 dispatch_semaphore_signal(sem); }]; // 等待信号,同时持续让渡主线程执行权处理事件,完全等效原runUntilDate的运行逻辑 while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } // 原有后续业务代码直接放在此处即可,无需修改
方案说明
- 原
runUntilDate方案失效的核心原因是iOS15调整了动画调度优先级,硬卡固定时长和实际动画完成时间不匹配,本方案通过监听动画实际完成的信号触发后续逻辑,不受系统版本调度逻辑影响 - 循环内调用
runMode:beforeDate:的逻辑和原runUntilDate完全一致,会主动释放主线程资源处理UI交互、系统回调等事件,不会出现界面无响应的问题 - 改造成本极低,仅需要在原有动画逻辑基础上增加3行代码即可,不需要调整原有业务嵌套结构
兜底优化(可选)
如果担心动画被用户交互打断、永远触发不了completion的极端场景,可以增加超时兜底逻辑:
dispatch_semaphore_t sem = dispatch_semaphore_create(0); // 超时时间设置为动画时长的1.2~1.5倍即可,可按需调整 dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)); [UIView animateWithDuration:0.7f animations:^{ // 原有动画逻辑 } completion:^(BOOL finished) { dispatch_semaphore_signal(sem); }]; while (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; // 超时则跳出等待,避免卡死 if (dispatch_time(DISPATCH_TIME_NOW, 0) > timeout) { break; } }
备选方案(dispatch_group实现)
如果不想使用信号量,也可以用dispatch_group实现,逻辑完全一致:
dispatch_group_t animGroup = dispatch_group_create(); dispatch_group_enter(animGroup); [UIView animateWithDuration:0.7f animations:^{ // 原有动画逻辑 } completion:^(BOOL finished) { dispatch_group_leave(animGroup); }]; while (dispatch_group_wait(animGroup, DISPATCH_TIME_NOW)) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }
内容的提问来源于stack exchange,提问作者Starman
相关产品推荐
相关产品推荐

