我想使用一个按钮作为切换按钮——单击一次,图像就会无限期地旋转。再次点击,图像停止,再次点击,它重新开始。
我发现这个答案有助于让动画继续: Rotate a view for 360 degrees indefinitely in Swift?
但是,我不清楚如何停止。我已经实现了下面的代码并且它似乎可以工作,但我很好奇这是否是停止动画的正确方法,或者是否有另一种首选方法。另外 - 我的旋转一直持续到完成,但我想知道我是否可以在按下按钮时将旋转卡住在某个位置(我在下面的第二次尝试中尝试了 .removeAllAnimations() ,但这似乎不起作用全部。
@IBOutlet weak var imageView: UIImageView!
var stopRotation = true
func rotateView(targetView: UIView, duration: Double = 1.0) {
if !stopRotation {
UIView.animate(withDuration: duration, delay: 0.0, options: .curveLinear, animations: {
targetView.transform = targetView.transform.rotated(by: CGFloat(Double.pi))
}) { finished in
self.rotateView(targetView: targetView, duration: duration)
}
}
}
@IBAction func spinPressed(_ sender: UIButton) {
stopRotation = !stopRotation
if !stopRotation {
rotateView(targetView: imageView)
}
}
这确实有效。我还想知道是否有可能在旋转过程中停止动画。按照它的设置方式,动画在停止前旋转了 180 度。我还尝试在 spinPressed 操作中添加一个 removeAnimation,并在 rotateView 中删除 stopRotation 检查,但这似乎不起作用 - 旋转继续并且如果再次按下 spinPressed 只会变得更快(见下文):
@IBOutlet weak var imageView: UIImageView!
var stopRotation = true
func rotateView(targetView: UIView, duration: Double = 1.0) {
UIView.animate(withDuration: duration, delay: 0.0, options: .curveLinear, animations: {
targetView.transform = targetView.transform.rotated(by: CGFloat(Double.pi))
}) { finished in
self.rotateView(targetView: targetView, duration: duration)
}
}
@IBAction func spinPressed(_ sender: UIButton) {
stopRotation = !stopRotation
if stopRotation {
imageView.layer.removeAllAnimations()
} else {
rotateView(targetView: imageView)
}
}
欢迎确认第一种方法是否合理。如果有一种方法可以在旋转中途停止旋转,那也很受欢迎(同时让我直截了本地了解我对 removeAllAnimations 的错误想法)。
谢谢! JG
最佳答案
有几种方法可以满足您的要求:
如果支持 iOS 10+,您可以使用
UIViewPropertyAnimator
,您可以暂停和重新启动它的动画(从暂停的地方恢复):private var animator: UIViewPropertyAnimator? deinit { animator?.stopAnimation(true) } @IBAction func didTapButton(_ sender: Any) { guard let animator = animator else { createAnimation() return } if animator.isRunning { animator.pauseAnimation() } else { animator.startAnimation() } } /// Create and start 360 degree animation /// /// This will fire off another animation when one 360° rotation finishes. private func createAnimation() { animator = UIViewPropertyAnimator.runningPropertyAnimator(withDuration: 4, delay: 0, options: .curveLinear) { [self] in UIView.animateKeyframes(withDuration: 4, delay: 0) { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .init(rotationAngle: .pi * 2 * 1 / 3) } UIView.addKeyframe(withRelativeStartTime: 1.0 / 3.0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .init(rotationAngle: .pi * 2 * 2 / 3) } UIView.addKeyframe(withRelativeStartTime: 2.0 / 3.0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .identity } } } completion: { [weak self] _ in self?.createAnimation() } }
您也可以使用 UIKit Dynamics 来旋转项目。然后,您可以删除执行旋转的
UIDynamicItemBehavior
,它就停在原处。它会自动将 Viewtransform
留在原处。然后,要恢复旋转,只需再次为旋转添加一个UIDynamicItemBehavior
:private lazy var animator = UIDynamicAnimator(referenceView: view) private var rotate: UIDynamicItemBehavior! @IBAction func didTapButton(_ sender: Any) { if let rotate = rotate { animator.removeBehavior(rotate) self.rotate = nil } else { rotate = UIDynamicItemBehavior(items: [animatedView]) rotate.allowsRotation = true rotate.angularResistance = 0 rotate.addAngularVelocity(1, for: animatedView) animator.addBehavior(rotate) } }
这不会让您轻松地控制时间方面的旋转速度,而是它由
angularVelocity
决定,但这是一个很好的简单方法(并支持 iOS 7.0 及更高版本)。停止动画并将其留在停止位置的老式方法是捕获动画的
presentationLayer
(它显示它在飞行途中的位置)。然后您可以获取当前状态、停止动画并将变换设置为presentationLayer
报告的内容。private var isAnimating = false @IBAction func didTapButton(_ sender: Any) { if isAnimating { let transform = animatedView.layer.presentation()!.transform animatedView.layer.removeAllAnimations() animatedView.layer.transform = transform } else { let rotate = CABasicAnimation(keyPath: "transform.rotation") rotate.byValue = 2 * CGFloat.pi rotate.duration = 4 rotate.repeatCount = .greatestFiniteMagnitude animatedView.layer.add(rotate, forKey: nil) } isAnimating = !isAnimating }
如果您想使用基于
UIView
block 的动画,您必须捕获停止动画的角度,以便知道从哪里重新开始动画。诀窍是获取CATransform3D
的m12
和m11
:angle = atan2(transform.m12, transform.m11)
因此,这会产生:
private var angle: CGFloat = 0 private var isAnimating = false @IBAction func didTapButton(_ sender: Any) { if isAnimating { let transform = animatedView.layer.presentation()!.transform angle = atan2(transform.m12, transform.m11) animatedView.layer.removeAllAnimations() animatedView.layer.transform = transform } else { UIView.animate(withDuration: 4, delay: 0, options: .curveLinear) { [self] in UIView.animateKeyframes(withDuration: 4, delay: 0, options: .repeat) { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .init(rotationAngle: self.angle + .pi * 2 * 1 / 3) } UIView.addKeyframe(withRelativeStartTime: 1.0 / 3.0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .init(rotationAngle: self.angle + .pi * 2 * 2 / 3) } UIView.addKeyframe(withRelativeStartTime: 2.0 / 3.0, relativeDuration: 1.0 / 3.0) { animatedView.transform = .init(rotationAngle: self.angle) } } } } isAnimating = !isAnimating }
您可以使用
CADisplayLink
自行旋转对象,将角度更新为某个计算值。然后停止旋转就像使显示链接无效一样简单,从而将其留在停止时的位置。然后,您可以通过简单地将显示链接添加回您的运行循环来恢复动画。这种技术为您提供了很大的控制权,但却是最不优雅的方法。
关于swift - 停止无限旋转图像的正确方法?以及如何实现 removeAllAnimations?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46208916/