目前,我正在使用一个按钮来激活一个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()
})
}
}
最佳答案
这里有一些事情,所以让我先给你一些背景知识,然后再看看发生了什么,看看哪里出了问题。
扰流器:它是isRemovedOnCompletion = false
和向前fillMode
的组合。
一些背景
在iOS上,每个视图都有自己拥有和管理的层。设置视图的frame
或transform
等属性会自动设置层的frame
或transform
。对于层,设置的该值有时称为“模型值”,以将其与“表示值”区分开来,表示值是在进行中的动画期间层在屏幕上的显示方式。实际上,这意味着两件事:
如果在使用UIView动画设置视图动画时试图检查层位置的值,则position
值将是动画块中指定的结束值。要获得屏幕上显示的“当前”值,必须查看层的表示层的值。
如果向层添加显式动画,则该层将在屏幕上设置更改的动画,但如果在动画期间检查动画属性,则该属性将保持不变。动画完成并移除后,层将再次渲染其模型值,使其看起来好像跳回了旧值,除非该值也已更改。
在这个案子里发生了什么
出发
视图(及其层)以(10, 10)
的帧原点和(100, 100)
的大小开始,使其成为center
(层的position
)(60, 60)
。由于未设置转换,因此使用“标识转换”(无转换)。
触发动画
当按下按钮时,将执行动画块,该动画块将视图(以及其层)的变换更改为平移(150, 200)
缩放(1.5, 1.5)
旋转π/2
的变换。这将更改层的transform
“model”值。
运行完成块
第一个动画完成后,将触发第二个动画。此时,模型值仍然是动画块中设置的值。
将三个变换动画配置为动画到(10, 10)
的平移、-π
的旋转和1
的缩放。这些动画的组配置为在完成时不删除。此时不更新模型值。
动画组完成
动画组完成,但不会删除。此时,模型值仍然是在原始动画块中设置的变换。不删除CAAnimation会保留模型(属性是什么)和表示(它在屏幕上的显示方式)之间的差异。
注意,此时position
应该是(60, 60)
,因为它从未更改过。更改transform
属性不会更改position
属性,只会更改显示在屏幕上的层。
第一个动画再次触发
下一次按下按钮时,将再次触发第一个动画。它将从“旧”值动画到“新”值。因为(模型的)属性从未改变,所以它们是相同的。
我不记得它是否有文档记录,但UIView动画在向层添加动画时(发生在幕后)使用已更改的属性作为键。
因为第二个动画已添加到关键帧transform
的层,并且该层每个唯一关键帧只能有一个动画。将删除组动画。这使得视图看起来像是跳回第一个动画的“结束值”。
据我所知,UIKit正在做什么,然后视图将执行从一个值到同一个值的3秒动画(即不更改任何内容)。
UIView动画完成后,完成块再次运行第二个动画,将组动画添加到层中。
如何修复
真正的问题是这两行:
groupTransform.isRemovedOnCompletion = false
groupTransform.fillMode = kCAFillModeForwards
它们一起看起来好像做了正确的事情,因为它在屏幕上看起来是正确的,但是正如您在上面看到的,它们在动画完成后很长时间内保持了模型和表示层之间的临时差异。
一个好的经验法则是:完成时不移除动画的“唯一”时间是,如果动画正在设置视图/层的消失,并且视图/层在完成时被移除。
一种解决方法是删除这两行,然后在将动画组添加到层之前,还必须将视图(或层)的变换更新为新的
"transform"
值。但是,在这种情况下,可以使用来自完成块内的另一个UIView动画来实现相同的动画。或者,可以使用UIView关键帧动画,该动画包含两个3秒的步骤。
尽可能地坚持使用UIView动画有一个好处,那就是模型值会被更新。
关于ios - 动画移回原始位置后未调用UIView动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44727262/