为什么最终呈现的动画效果不尽如人意?

Why Is the Final Presentation of the Animation's Result Not What Is Expected?

以下 gif 演示了我遇到的动画问题。红色的小视图正在动画化。绿色的小视图标记了初始位置。红色视图动画向下和向右,然后自动反转。最后,回到初始位置后,红色视图向左跳转:就是这个跳转,我看不懂。

这是代码。动画分为两部分:Y 位置的初始变化和中途开始的 X 位置变化。两种动画都是自动反转的。请注意更新模型以反映由自动反转带来的最终视图位置的完成闭包。这一切对我来说似乎都是正确的。为什么向左跳?

    @objc private func animate() {

        let center = animationView.center

        let distance: CGFloat = 100
        let down = {
            UIView.setAnimationRepeatAutoreverses(true)
            self.animationView.center.y += distance
        }
        let right = {
            UIView.setAnimationRepeatAutoreverses(true)
            self.animationView.center.x += distance
        }

        let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: down)
        animator.addAnimations(right, delayFactor: 0.5)
        animator.addCompletion { _ in self.animationView.center = center } // Sync up with result of the autoreverse
        animator.startAnimation()
    }
  1. 向左的跳跃似乎是 100 点(与动画位置变化相同)
  2. 问题似乎与动画的第二部分有关。如果我不添加它而是在第一部分(修改 Y 的地方)修改 X 那么一切都很好。
  3. 当动画完成后,我检查红色视图的中心时发现它是 "correct";它等于根视图的中心。也就是说:模型和演示文稿不符???
  4. 当我使用 Xcode 的调试视图层次结构功能时,红色视图显示为位于屏幕中央

// ********************************************** ****************************

更新 #1:动画由两部分组成;向下和向右。在这两部分中,我都调用了 UIView.setAnimationRepeatAutoreverse。如果我注释掉其中一个或两个自动反转,那么一切都会按预期工作(尽管我没有得到自动反转的效果,但代码的行为是可以理解的)。

// ********************************************** ****************************

更新 #2:来自 Apple 的 UIView.setAnimationRepeatAutoreverse 文档:iOS 4.0 及更高版本不鼓励使用此方法。相反,您应该使用 animate(withDuration:delay:options:animations:completion:) 方法来指定动画和动画选项。

好的。让我们看看还有什么可以用来完成动画第二部分的延迟。

使用旧的API

    @objc private func animate() {

        let original = animationView.center

        let distance: CGFloat = 100
        let down = { self.animationView.center.y += distance }
        let right = { self.animationView.center.x += distance }

        let duration: TimeInterval = 2
        UIView.animate(withDuration: duration, delay: 0, options: .autoreverse, animations: down) { _ in self.animationView.center.y = original.y }
        UIView.animate(withDuration: duration/2, delay: duration/2, options: .autoreverse, animations: right) { _ in self.animationView.center.x = original.x }
    }

终于来了!

private func animate() {

    let distance: CGFloat = 100
    let down = { self.animationView.center.y += distance }
    let right = { self.animationView.center.x += distance }

    let animator = UIViewPropertyAnimator(duration: 2, curve: .linear, animations: down)
    animator.addAnimations(right, delayFactor: 0.5)
    animator.pausesOnCompletion = true
    let observer = animator.observe(\.isRunning, options: [.new] ) { animator, change in
        print("isRunning changed to \(change.newValue!).")

        if !animator.isRunning {
            if !animator.isReversed {
                print("Reversing animation")
                animator.isReversed = true;
                animator.startAnimation()
            }
            else {
                print("Stopping animation")
                animator.stopAnimation(false)
                animator.finishAnimation(at: .start)
            }
        }
    }
    animator.addCompletion { position in
        print("Animation completed at \(position). State = \(animator.state).")
        observer.invalidate() // By capturing the observer here in the completion closure we keep it alive for the duration of the animation.
    }

    print("\nStarting animation")
    animator.startAnimation()
    print("Animation started")
}