通过将 .speed 设置为 -1

问题描述

编辑:我稍微重构了问题并解决了部分问题,现在问题归结为为什么在恢复动画时表示层会出现故障/闪烁。在这一点上,我接受任何可以使动画随意向前和向后恢复而没有问题的答案。我不确定我使用的方法是否正确,我对 Swift 还是很陌生。

注意底部的示例项目,用于更好地理解问题。

在我的项目中,我通过将图层 CABasicAnimation 属性设置为 .speed 来暂停 0,然后我通过设置图层的 {{1}每当用户滚动滑块时,} 属性就等于 .timeOffset UiSlider 属性。按代码

.value

然后当用户滑动时:

layer.speed = 0 

现在我想在滑块上的用户手势结束时随意向后或向前恢复动画,因此从与当前动画值相关的起点开始。我发现运行平稳的唯一可行解决方案如下,但它只能向前工作:

layer.timeOffset = CFTimeInterval(sender.value)

然后我可以在动画完成时再次暂停它:

let pausedTime = layer.timeOffset
layer.speed = 1.0
layer.timeOffset = 0.0
layer.beginTime = 0.0
let timeSincePause = layer.convertTime(CACurrentMediaTime(),from: nil) - pausedTime
layer.beginTime = timeSincePause

根据我的理解, dispatchQueue.main.asyncAfter(deadline: .Now()+1) { layer.timeOffset = 0 layer.speed = 0 } 不仅定义了动画的实际速度,结合 .speed 属性,还定义了动画的方向:如果我将图层的速度设置为等于 {{1 }} 然后动画向后完成。参考关于 .duration 如何工作的 this 答案,我试图更改上面代码片段的参数以恢复动画向后倒退,但没有运气。我认为这会奏效:

-1

但是图层从来没有像这样动画过。该问题似乎与 camediatiming 方法有关。

然后我发现 this 问题与我的基本相同,唯一的答案有一个不错的解决方案。重构一下代码,我只能说:

let pausedTime = layer.timeOffset
layer.timeOffset = 0.0
layer.beginTime = 0.0
let timeSincePause = layer.convertTime(CACurrentMediaTime(),from: nil) - pausedTime
layer.beginTime = timeSincePause
layer.timeOffset = pausedTime*2
layer.speed = -1.0

然而,当动画向后播放时会出现问题,特别是表示层在恢复和完成时都会闪烁。我尝试了各种解决方案但没有运气,我做出了一些推测:

  • 这可能是与 convertTime 相关的问题,因为我可以将其设置为 layer.beginTime = CACurrentMediaTime() layer.speed = -1 dispatchQueue.main.asyncAfter(deadline: .Now()+1) { layer.timeOffset = 0 layer.speed = 0 } camediatimingFillMode,但是当它恢复时,动画既不是最终状态也不是初始状态,因此不渲染初始帧;
  • 这是因为我没有保持模态树和表示树同步。

然而,当它在恢复和完成时闪烁/闪烁时,这两个都没有解释。此外,在我看来,动画在向前恢复时的持续时间可能为 1,但在向后恢复时只有 1-timeOffset,不确定。

真的不确定实际问题是什么以及如何解决这个烂摊子。我们非常欢迎所有建议。

对于任何感兴趣的人,这里有一个类似于我的示例项目,其灵感来自另一个问题(动画向前运行,向后运行并捕获故障只需调用 .back)。我知道应该重构代码,但仍然可以达到目的。只需复制、粘贴和运行:

.forwards

解决方法

我设法消除了示例项目中 resumeLayerBackwards(layer:) 的故障。 实际上有两个问题:

  1. 动画视觉完成后出现一个空白屏幕
  2. 空白屏幕显示 1 - .timeOffset

所以,问题似乎在于动画实际上不仅在 .timeOffset 期间播放,而且在整个 .duration 期间播放。出现空屏幕是因为没有为 1 - .timeOffset 块定义动画。

回想一下:CALayer 也采用 CAMediaTiming 协议,就像 CAAnimation 一样(定义了所有属性:尽管其中一些似乎不太清楚如何应用于层)。

speed = -1 秒过后 .timeOffset — 属性 .timeOffset 变为零。这意味着动画已经开始,因此(以负速度)它完成了。虽然它不是那么明显 - 似乎因为 .fillMode 属性而被删除。为了解决这个问题,我在 perspectiveLayer.fillMode = .forwards 方法中添加了 animate()

要让动画恰好在 .timeOffset 秒后完成而不是整个 .duration — 使用 .repeatDuration 属性。我已将 layer.repeatDuration = layer.timeOffset 添加到您的 resumeLayerBackwards(layer:) 方法中。

该项目仅适用于添加了两行。

我不能说这个解决方案对我来说真的合乎逻辑,尽管它有效。负速度对我来说有点不可预测。在我的项目中,我曾经通过交换克隆动画对象中的开始和结束值来反转动画。