UIView 动画在转换到新视图之前快速恢复到原始状态

Posted

技术标签:

【中文标题】UIView 动画在转换到新视图之前快速恢复到原始状态【英文标题】:UIView animation snaps back to original state before transitioning to a new view 【发布时间】:2019-10-03 13:44:01 【问题描述】:

我有一些 UIButtons 我正在无限期地制作动画。这些按钮都添加了 3 个子层,每个子层都有自己的动画。我正在viewDidAppear 上初始化这些动画,效果很好——它们淡入并开始旋转。问题是当我转换到一个新视图时,动画似乎“快速”回到它们的初始状态,然后在转换发生之前回到其他状态。我已经尝试显式删除 viewWillDisappear 上的所有动画,甚至尝试隐藏整个 UIButton 本身,但似乎没有什么能阻止这种奇怪的捕捉行为发生。

这是正在发生的事情的 gif 图像(这是我在两个视图之间来回转换):

func animateRotation() 
// Gets called on viewDidAppear

    let rotationRight: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
    rotationRight.toValue = Double.pi * 2
    rotationRight.duration = 4
    rotationRight.isCumulative = true
    rotationRight.repeatCount = Float.greatestFiniteMagnitude
    rotationRight.isRemovedOnCompletion = false
    rotationRight.fillMode = .forwards

    let rotationLeft: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
    rotationLeft.toValue = Double.pi * -2
    rotationLeft.duration = 3
    rotationLeft.isCumulative = true
    rotationLeft.repeatCount = Float.greatestFiniteMagnitude
    rotationLeft.isRemovedOnCompletion = false
    rotationLeft.fillMode = .forwards

    let circleImage1 = UIImage(named: "circle_1")?.cgImage
    circleLayer1.frame = self.bounds
    circleLayer1.contents = circleImage1
    circleLayer1.add(rotationRight, forKey: "rotationAnimation")
    layer.addSublayer(circleLayer1)

    let circleImage2 = UIImage(named: "circle_2")?.cgImage
    circleLayer2.frame = self.bounds
    circleLayer2.contents = circleImage2
    circleLayer2.add(rotationLeft, forKey: "rotationAnimation")
    layer.addSublayer(circleLayer2)

    let circleImage3 = UIImage(named: "circle_3")?.cgImage
    circleLayer3.frame = self.bounds
    circleLayer3.contents = circleImage3
    circleLayer3.add(rotationRight, forKey: "rotationAnimation")
    layer.addSublayer(circleLayer3)

我认为这样简单的事情会在它知道按钮消失后立即将其完全隐藏:

override func viewWillDisappear(_ animated: Bool) 
    animatedButton.isHidden = true

有趣的是,如果我让它运行几分钟,这似乎就不会发生了。这告诉我这可能是某种竞争条件。只是不确定那场比赛可能是什么......

【问题讨论】:

过渡到新视图时,您的意思是转到新屏幕并返回吗?你试过在 ViewDidLoad() 中调用 animateRotation() 吗?是否有必要在 viewDidAppear() 中? 用动画进入视图时效果很好。我看到的奇怪行为是立即离开屏幕并激活动画。所以我点击按钮离开屏幕,动画闪烁,然后屏幕切换到下一个视图。 你能显示点击按钮时触发的代码吗?试图了解您的视图控制器的结构。所以当屏幕加载时动画总是可见的?显示您点击按钮的 gif 图像可能会很有用,这样我就可以了解您的视图的外观和工作方式。动画似乎同时运行了多次。 @AlecSanger “有趣的是,如果我让它运行几分钟,这似乎就不会发生了。”我想知道这是否与动画的“.duration”属性有关。也许尝试将持续时间设置为 15 秒,看看错误是否发生在 15 秒之前而不是之后。 当按钮被点击时实际上什么都没有发生——你可以考虑将这些普通的 UIViews 应用为子层的图像。将动画设置为仅运行一次时,旋转会正确停止在最后的静止位置(1 个完整旋转)。当导航离开屏幕时,没有闪烁,因为闪烁在当前动画位置和图层的原始位置之间移动。如果我让动画完成,这些都是一样的。 【参考方案1】:

当您使用 UIView 动画集时,它们会将实际状态设置为最终状态,然后启动动画,即:

UIView.animate(withDuration: 1)  view.alpha = 0 

如果您在动画开始后立即检查 alpha,它已经是 0。

这是 UIView 为您提供的便利,但 CALayer 没有。当您设置 CALayer 动画时,您只是在设置动画值,而不是变量的实际值,因此当动画完成时,图层会恢复到其原始值。在动画中间检查图层值,你会看到真实的值没有改变;只有表示层中的动画值发生了变化。

如果你想复制 UIView 行为,你需要在动画开始时设置实际的最终值而不是图层,或者在动画结束时使用委托来设置它。

【讨论】:

你所说的一切都完全有道理,我很确定你的建议会解决问题。但是,我继续使用 UIView.animate + self.transform 将我的 3 个单独动画子层替换为单个旋转 UIButton,并在其上应用单个图像,并且我仍然看到相同的行为。这告诉我这与 CALayer 无关。

以上是关于UIView 动画在转换到新视图之前快速恢复到原始状态的主要内容,如果未能解决你的问题,请参考以下文章

表格视图中的 UIView 转换

UIView 动画打破 TableView

将 UITableViewControllers 表视图添加到新的 UIView

旋转设备时,动画 uiview 切换回其原始状态

左右滑动 UIView

当视图消失时,UIView动画停止,重新出现时不再恢复