如何实现类似UIActivityIndicatorView的自定义旋转闪烁动画指示器
我来帮你把这个自定义活动指示器的实现补全,要达到和UIActivityIndicatorView一样的旋转闪烁效果,我们需要完善绘制逻辑、组合动画这几块内容,直接看完整实现和说明:
自定义旋转闪烁活动指示器实现
这个实现核心是多个色块按顺序闪烁+视图整体旋转的组合动画,和系统Indicator的效果对齐,同时保留可配置性。
1. 完整代码实现
import UIKit class MyIndicator: UIView { // 可配置参数,根据需求调整 private let segmentCount = 12 // 指示器的色块数量 private let segmentColor: UIColor = .systemBlue // 色块颜色 private let animationDuration: CFTimeInterval = 1.2 // 一轮闪烁动画时长 private let gap: CGFloat = .pi/4 / 6.0 // 每个色块之间的间隙角度 // 保存动画组,用于后续停止动画时清理 private var animationGroups: [CAAnimationGroup] = [] override init(frame: CGRect) { super.init(frame: frame) setupBaseConfig() } required init?(coder: NSCoder) { super.init(coder: coder) setupBaseConfig() } // 初始化基础配置 private func setupBaseConfig() { backgroundColor = .clear isHidden = true // 默认隐藏,启动动画时再显示 } // 绘制指示器的所有色块 override func draw(_ rect: CGRect) { super.draw(rect) let center = CGPoint(x: bounds.midX, y: bounds.midY) // 色块半径,留边距避免超出视图范围 let radius = min(bounds.width, bounds.height) / 2 * 0.7 // 计算每个色块的角度(总圆周减去所有间隙后平分) let segmentAngle = (2 * .pi - CGFloat(segmentCount) * gap) / CGFloat(segmentCount) for i in 0..<segmentCount { let startAngle = CGFloat(i) * (segmentAngle + gap) let endAngle = startAngle + segmentAngle // 绘制单个扇形色块 let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true) segmentColor.setFill() path.fill() } } // 启动动画(包含闪烁+旋转) func startAnimating() { isHidden = false cleanExistingAnimations() // 先清理旧动画,避免重复叠加 let center = CGPoint(x: bounds.midX, y: bounds.midY) let radius = min(bounds.width, bounds.height) / 2 * 0.7 let segmentAngle = (2 * .pi - CGFloat(segmentCount) * gap) / CGFloat(segmentCount) // 给每个色块单独创建图层和闪烁动画 for i in 0..<segmentCount { let startAngle = CGFloat(i) * (segmentAngle + gap) let endAngle = startAngle + segmentAngle let segmentLayer = CAShapeLayer() segmentLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true).cgPath segmentLayer.fillColor = segmentColor.cgColor layer.addSublayer(segmentLayer) // 透明度动画:从淡到亮再回到淡,模拟闪烁 let opacityAnim = CAKeyframeAnimation(keyPath: "opacity") opacityAnim.values = [0.1, 1.0, 0.1] opacityAnim.keyTimes = [0, 0.5, 1] opacityAnim.duration = animationDuration opacityAnim.repeatCount = .infinity // 每个色块延迟启动动画,实现顺序闪烁的效果 let delay = (animationDuration / Double(segmentCount)) * Double(i) opacityAnim.beginTime = CACurrentMediaTime() + delay // 动画组统一管理 let animGroup = CAAnimationGroup() animGroup.animations = [opacityAnim] animGroup.duration = animationDuration animGroup.repeatCount = .infinity animGroup.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) segmentLayer.add(animGroup, forKey: "blinkAnimation") animationGroups.append(animGroup) } // 添加整体旋转动画 let rotateAnim = CABasicAnimation(keyPath: "transform.rotation.z") rotateAnim.fromValue = 0 rotateAnim.toValue = 2 * .pi rotateAnim.duration = animationDuration * 2 // 旋转速度可调整 rotateAnim.repeatCount = .infinity layer.add(rotateAnim, forKey: "rotateAnimation") } // 停止动画并清理资源 func stopAnimating() { isHidden = true cleanExistingAnimations() } // 清理所有动画和子图层 private func cleanExistingAnimations() { layer.removeAllAnimations() layer.sublayers?.forEach { $0.removeAllAnimations() } layer.sublayers = nil animationGroups.removeAll() } }
2. 使用方式
和系统的UIActivityIndicatorView用法一致,非常简单:
// 创建指示器(设置合适的大小) let customIndicator = MyIndicator(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) customIndicator.center = view.center view.addSubview(customIndicator) // 启动动画 customIndicator.startAnimating() // 停止动画(比如网络请求完成后) // customIndicator.stopAnimating()
3. 可自定义调整的点
- 修改
segmentCount可以调整色块的数量,系统默认是12个 - 修改
segmentColor可以替换指示器的颜色 - 调整
animationDuration可以改变动画的快慢 - 修改
gap可以调整色块之间的间隙大小
内容的提问来源于stack exchange,提问作者jpulikkottil
相关产品推荐
相关产品推荐

