UIView动画完成块提前触发及视图控制器交互转场问题咨询
这种转场交互中动画完成块提前触发的问题我之前也碰到过,结合你的场景(自定义动画器+转场时修改模糊效果),大概率是这几个原因导致的,给你几个排查和解决的方向:
1. 解决隐式动画与显式动画的冲突
当你直接设置visualEffectView.effect = nil时,UIKit默认会给这个属性变化加上隐式动画,如果你同时又用了UIView.animate的显式动画,两者会互相干扰,很容易导致完成块被提前触发。建议在动画块内临时禁用隐式动画:
UIView.animate(withDuration: 0.3, animations: { UIView.setAnimationsEnabled(false) self.blurView.effect = nil UIView.setAnimationsEnabled(true) }, completion: { finished in // 重点:一定要关注finished参数——转场被取消/中断时这个值是false,别误把它当成动画正常结束的标志 if finished { // 这里写动画正常完成后的逻辑 } })
2. 将动画绑定到转场的专属上下文
自定义转场时,系统会创建一个专属的UIViewControllerContextTransitioning动画上下文,如果你用普通的UIView.animate创建独立动画,它不在转场的生命周期管控范围内,当转场进度变化或被取消时,系统会直接终止这个“游离”的动画,导致完成块提前调用。
正确的做法是在自定义动画器的animateTransition(using:)方法里,依托转场上下文添加动画:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from), let toVC = transitionContext.viewController(forKey: .to) else { return } let containerView = transitionContext.containerView // 处理转场的其他核心动画逻辑... // 把模糊效果动画绑定到转场上下文 UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { fromVC.blurView.effect = nil }, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) }
这样动画就和转场的生命周期强绑定了,只有转场真正完成或取消时,对应的逻辑才会触发。
3. 修正通知的触发时机
你用通知触发动画的方式,很可能存在时机偏差——比如用户刚开始拖拽返回(转场还在交互阶段)就发送了通知,此时启动的动画会被后续的转场进度变化打断。
建议直接在自定义动画器的animateTransition(using:)或startInteractiveTransition(_:)方法里触发模糊动画,完全抛弃通知,这样时机更精准,也能避免跨组件的状态同步问题。
4. 避免重复动画的干扰
如果转场被取消后再次触发,之前未完成的动画可能还残存在视图层级里,新动画和旧动画冲突会导致完成块异常。可以在启动新动画前,先清理视图上的所有残留动画:
self.blurView.layer.removeAllAnimations() // 再启动新的动画 UIView.animate(withDuration: 0.3) { self.blurView.effect = nil }
内容的提问来源于stack exchange,提问作者Tigran Iskandaryan

