如何实现ImageView无限循环旋转与缩放,并通过按钮点击重置?
解决ImageView无限循环旋转+缩放动画问题,附重置功能
首先得指出你代码里的核心问题:
- 在同一个
animations闭包里连续赋值self.transform,后面的变换会直接覆盖前面的,所以第一个动画块里只有缩放效果生效,旋转完全被忽略了。 - 递归调用时,transform的累积逻辑混乱,比如第二次动画直接把缩放设回1.0,但旋转是基于上一次的transform继续叠加,导致动画的叠加效果不符合预期。
正确的实现方式(两种可选)
方式一:利用UIView动画的自动反转+重复选项(简洁版)
这种方式适合不需要手动控制每一轮细节的场景,系统会自动处理循环和反转逻辑:
func startLoopAnimation(duration: Double = 1.0) { // 先重置transform,避免之前的动画残留影响初始状态 self.transform = .identity UIView.animate(withDuration: duration, delay: 0, options: [.curveLinear, .autoreverse, .repeat]) { // 把旋转和缩放变换组合起来,确保两个效果同时生效 let rotateTransform = CGAffineTransform(rotationAngle: .pi) let scaleTransform = CGAffineTransform(scaleX: 0.5, y: 0.5) self.transform = rotateTransform.concatenating(scaleTransform) } }
这里用concatenating将旋转(180度)和缩放(0.5倍)的变换合并,.autoreverse会让动画执行完后反向回到初始状态,.repeat让这个过程无限循环,完美实现你要的旋转180度+缩放到0.5 → 再旋转180度(总共一圈)+ 缩回到1.0的循环效果。
方式二:递归手动控制每一轮动画(灵活可控版)
如果需要更精细的控制每一步动画流程,可以用递归的方式,每次动画完成后手动触发下一轮,同时正确处理变换的累积:
func startRecursiveLoopAnimation(duration: Double = 1.0) { // 第一步:旋转180度+缩放到0.5 UIView.animate(withDuration: duration, delay: 0, options: .curveLinear) { let rotate = CGAffineTransform(rotationAngle: .pi) let scale = CGAffineTransform(scaleX: 0.5, y: 0.5) self.transform = rotate.concatenating(scale) } completion: { [weak self] finished in guard let self = self else { return } // 第二步:再旋转180度(总共一圈)+ 缩回到1.0 UIView.animate(withDuration: duration, delay: 0, options: .curveLinear) { // 基于当前transform继续旋转π,同时缩放回1.0 let rotate = self.transform.rotated(by: .pi) let scale = CGAffineTransform(scaleX: 1.0, y: 1.0) self.transform = rotate.concatenating(scale) } completion: { [weak self] finished in guard let self = self else { return } // 递归调用,开启下一轮循环 self.startRecursiveLoopAnimation(duration: duration) } } }
这里用[weak self]避免循环引用,每一步都正确组合变换,确保旋转和缩放效果同时生效。
按钮点击重置动画的实现
要重置动画,需要先停止当前所有动画,然后把ImageView的transform重置为初始状态,再重新启动动画:
@IBAction func resetAnimationButtonTapped(_ sender: UIButton) { // 停止所有正在进行的动画 imageView.layer.removeAllAnimations() // 重置transform到初始状态 imageView.transform = .identity // 重新启动动画(根据你选的方式调用对应的方法) imageView.startLoopAnimation() // 或者调用 startRecursiveLoopAnimation() }
额外提示
- 每次启动动画前,建议先把transform重置为
.identity,避免之前的动画残留导致初始状态异常。 - 如果使用方式一的
.repeat选项,停止动画同样用layer.removeAllAnimations(),然后重置transform即可。
内容的提问来源于stack exchange,提问作者Asif Raza
相关产品推荐
相关产品推荐

