目前我正在使用一个按钮来激活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()
})
}
}
答案 0 :(得分:1)
这里有一些事情发生,所以让我先给你一些背景知识,然后仔细研究发生的事情,看看哪里出了问题。
剧透:它是isRemovedOnCompletion = false
和前锋fillMode
的组合。
在iOS上,每个视图都有一个它拥有和管理的图层。设置视图的frame
或transform
等属性会自动设置图层的frame
或transform
。对于图层,设置的此值有时称为"模型值"区别于"演示价值"这是在正在进行的动画期间图层在屏幕上的显示方式。在实践中,这意味着两件事:
如果在视图使用UIView动画设置动画时尝试检查图层位置的值,则position
值将是结束值,该值已在动画块。来到"当前"在屏幕上显示的值,人们必须查看图层的表示层的值。
如果向图层添加显式CAAnimation,图层将在屏幕上为更改设置动画,但如果在动画期间检查动画属性,它将保持不变。一旦动画完成并被移除,该图层将再次呈现它的模型值,使其看起来好像它跳回到旧值,除非该值也被更改。
视图(及其图层)以(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
})
}