在动画移回原始位置后,不会调用UIView动画

时间:2017-06-23 17:48:07

标签: ios swift core-animation uiviewanimation cabasicanimation

目前我正在使用一个按钮来激活UIView动画,在它的完成块中,我正在使用CABasicAnimation将其返回到它的起始位置。动画首先运行平稳并返回其原始起点,但是当我再次按下按钮时,动画会自动从原始UIView动画结束的位置开始,而假设它从原始起点开始。

class ObjectCreateMoveViewController: UIViewController, CAAnimationDelegate {

  let square = UIView()

  override func viewDidLoad() {
    super.viewDidLoad()

    createSquare()

  }

  func createSquare() {

    square.frame =  CGRect(x: 10, y: 10, width: 100, height: 100)
    square.backgroundColor = UIColor.red
    square.layer.borderColor = UIColor.blue.cgColor
    square.layer.borderWidth = 4
    square.layer.cornerRadius = 2
    view.addSubview(square)
  }


  func transformAnimation () {

    let transAnimation = CABasicAnimation(keyPath: "transform.translation")
    transAnimation.toValue = NSValue(cgPoint: CGPoint(x: 10, y: 10))

    let rotAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotAnimation.toValue = -CGFloat.pi

    let scaleAnimation = CABasicAnimation(keyPath: "transform.scale.xy")
    scaleAnimation.toValue = NSNumber(value: 1)

    let groupTransform = CAAnimationGroup()
    groupTransform.duration = 3
    groupTransform.delegate = self
    groupTransform.beginTime = CACurrentMediaTime()
    groupTransform.animations = [transAnimation, rotAnimation, scaleAnimation]
    groupTransform.isRemovedOnCompletion = false
    groupTransform.fillMode = kCAFillModeForwards
    groupTransform.setValue("circle", forKey: "transform")
    square.layer.add(groupTransform, forKey: "transform")
  }

  func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    if flag {
      //should be (10,10)but when I printed out the ending positions it was (60,60)
      square.layer.position.x = 60
      square.layer.position.y = 60
      print("This is the x point \(square.layer.position.x)")
      print("This is the y point \(square.layer.position.y)")
    }
  }

  @IBAction func transformButtonPressed(_ sender: Any) {

    UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut, animations: {

      let translateTransform = CGAffineTransform(translationX: 150, y: 200)
      self.square.transform = translateTransform.scaledBy(x: 1.5, y: 1.5).rotated(by: CGFloat.pi/2)

    }, completion: { _ in

      self.transformAnimation()

    })
  }
}

2 个答案:

答案 0 :(得分:1)

这里有一些事情发生,所以让我先给你一些背景知识,然后仔细研究发生的事情,看看哪里出了问题。

剧透:它是isRemovedOnCompletion = false和前锋fillMode的组合。

一些背景

在iOS上,每个视图都有一个它拥有和管理的图层。设置视图的frametransform等属性会自动设置图层的frametransform。对于图层,设置的此值有时称为"模型值"区别于"演示价值"这是在正在进行的动画期间图层在屏幕上的显示方式。在实践中,这意味着两件事:

  1. 如果在视图使用UIView动画设置动画时尝试检查图层位置的值,则position值将是结束值,该值已在动画块。来到"当前"在屏幕上显示的值,人们必须查看图层的表示层的值。

  2. 如果向图层添加显式CAAnimation,图层将在屏幕上为更改设置动画,但如果在动画期间检查动画属性,它将保持不变。一旦动画完成并被移除,该图层将再次呈现它的模型值,使其看起来好像它跳回到旧值,除非该值也被更改。

  3. 在这种情况下发生了什么

    开始

    视图(及其图层)以(10, 10)的框架原点和(100, 100)的大小突出显示,使其成为center(图层' s { {1}})position。由于没有设置变换,因此"身份变换" (没有变换)被使用。

    触发动画

    当按下按钮时,执行动画块,将视图(及其图层)的变换更改为(60, 60)缩放(150, 200)和旋转的变换(1.5, 1.5)。这改变了π/2"模型"层的价值。

    运行完成块

    第一个动画完成后,会触发第二个动画。此时,模型值仍然是动画块中设置的值。

    三种变换动画配置为动画为transform的翻译,(10, 10)的轮播和的缩放。这些动画组配置为在完成时删除。此时模型值更新。

    动画组完成

    动画组完成但未删除。此时,模型值仍然是在原始动画块中设置的变换。不删除CAAnimation持续存在模型(属性是什么)和表示(它在屏幕上的显示方式)之间的差异。

    请注意,此时1 预计为position,因为它从未更改过。更改(60, 60)属性不会更改 transform属性,仅限于图层显示在屏幕上的位置。

    第一个动画再次被触发

    下次按下该按钮时,将再次触发第一个动画。它将来自" old"对"新"的价值值。由于(模型的)position属性从未改变,因此它们是相同的。

    我无法回想起它是否有记录,但UIView动画使用在向图层添加动画时更改的属性(在幕后发生)。

    因为第二个动画已添加到键transform的图层中,并且图层每个唯一键只能有一个动画。组动画已删除。这使得视图看起来像跳回到"结束值"第一部动画。

    据我所知,UIKit正在做什么,视图将从一个值到同一个值执行3秒动画(即不改变任何东西)。

    UIView动画完成后,完成块再次运行第二个动画,将组动画添加到图层。

    如何解决

    真正的问题是这两行:

    "transform"

    他们看起来似乎做对了,因为它在屏幕上显得正确,但正如您在上面看到的那样,他们在动画完成后很久就会在模型和表示层之间保持暂时的差异。

    作为一个好的经验法则:只有"只有"完成时移除动画的时间是它是否为视图/图层的消失设置了动画,并且视图/图层在完成时被删除。

    解决修复方法的一种方法是删除这两行,然后处理必须同时更新视图(或图层)转换为新的groupTransform.isRemovedOnCompletion = false groupTransform.fillMode = kCAFillModeForwards 值将动画组添加到图层。

    然而,在这种情况下,可以使用完成块内的另一个UIView动画来实现相同的动画。或者,您可以使用具有两个3秒步长的UIView关键帧动画。

    在可能的情况下坚持使用UIView动画具有更新模型值的好处。

答案 1 :(得分:1)

David Rönnqvist为你提供了一个非常好的描述,但是如果你想要一个真正的,非常简单的&#34选项,那么它就会以它的方式动画回来#34; ...

选项1 - 使用.autoReverse

@IBAction func transformButtonPressed(_ sender: Any) {

    UIView.animate(withDuration: 3, delay: 0, options: [.curveEaseInOut, .autoreverse], animations: {

        let translateTransform = CGAffineTransform(translationX: 150, y: 200)
        self.square.transform = translateTransform.scaledBy(x: 1.5, y: 1.5).rotated(by: CGFloat.pi/2)

    }, completion: { _ in

        self.square.transform = .identity

    })
}

选项2 - 如果您想"将其发回"关于另一个行动,例如:

@IBAction func transformButtonPressed(_ sender: Any) {

    UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut, animations: {

        let translateTransform = CGAffineTransform(translationX: 150, y: 200)
        self.square.transform = translateTransform.scaledBy(x: 1.5, y: 1.5).rotated(by: CGFloat.pi/2)

    }, completion: { _ in

        // un-comment to animate it back right away,
        // or leave commented, and call transformBack() from somewhere else
        //self.transformBack()

    })
}

func transformBack() -> Void {

    UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut, animations: {

        // resets transform to "none"
        self.square.transform = .identity

    }, completion: { _ in

    })

}