1、用于网络请求、WebView的加载动画,效果如下图:
![](https://img.haomeiwen.com/i1910830/fdc2b1164c2a9e68.gif)
源码中有大量注释。Demo地址
1.1 实现思路
绘制三个球,给每个球添加动画组。以其中一个为例:
/// 开启第一个小球动画
private func startOneBallAnimation() {
// 旋转动画
let transformation = CAKeyframeAnimation(keyPath: "transform.translation.z")
transformation.values = [0, 1, 0, 0]
// 第一个小球位移动画
let oneFrameAnimation = CAKeyframeAnimation(keyPath: "position")
let path = CGMutablePath.init()
path.move(to: CGPoint(x: centerPoint.x - distance, y: centerPoint.y))
path.addLine(to: CGPoint(x: centerPoint.x, y: centerPoint.y))
path.addLine(to: CGPoint(x: centerPoint.x + distance, y: centerPoint.y))
path.addLine(to: CGPoint(x: centerPoint.x - distance, y: centerPoint.y))
oneFrameAnimation.path = path
oneFrameAnimation.keyTimes = [0, 0.333333, 0.666666, 1.0] as [NSNumber]
// 第一个小球缩放动画
let oneScaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
oneScaleAnimation.values = [0.7, 1.2, 0.7, 0.7]
// 第一个小球透明动画
let oneOpactiyAniamtion = CAKeyframeAnimation(keyPath: "opacity")
// 组合动画
let oneGroup = CAAnimationGroup()
oneGroup.animations = [transformation, oneFrameAnimation, oneScaleAnimation, oneOpactiyAniamtion]
oneGroup.duration = self.animationDurtion
oneGroup.repeatCount = HUGE
oneLayer.add(oneGroup, forKey: "oneGroup")
}
1.2 对外接口,设置动画属性:
// MARK: - Public Methods
/// 设置小球半径
func setBallRadius(radius: CGFloat) {
self.radius = radius
oneLayer.path = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true).cgPath
twoLayer.path = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true).cgPath
threeLayer.path = UIBezierPath(arcCenter: centerPoint, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true).cgPath
}
/// 设置三个小球颜色
func setBallColor(firstColor: UIColor, secondColor: UIColor, thirdColor: UIColor) {
oneLayer.fillColor = firstColor.cgColor
twoLayer.fillColor = secondColor.cgColor
threeLayer.fillColor = thirdColor.cgColor
}
/// 设置动画时长
func setAnimationDurtion(durtion: TimeInterval) {
self.animationDurtion = durtion
}
/// 设置小球移动轨迹的半径
func setAnimatonDistance(distance: CGFloat) {
self.distance = distance
if distance > bounds.width || distance < 4 {
self.distance = bounds.width / 2
}
}
/// 开始动画
func startAnimation() {
stopAnimation()
startOneBallAnimation()
startTwoBallAnimation()
startThreeBallAnimation()
}
/// 结束动画
func stopAnimation() {
// 不需要吧,这里又没有影响性能的
oneLayer.removeAllAnimations()
twoLayer.removeAllAnimations()
threeLayer.removeAllAnimations()
}
1.3 给UIView
写扩展,用户加载动画。makeLoading(title: String? = "", enable: Bool = false)
, 如果有title
,则特殊展示,没有则只展示动画。enable
标识动画展示期间界面是否可进行点击操作。
extension UIView {
/// 添加加载动画
///
/// - Parameters:
/// - title: 描述文字
/// - enable: 加载期间是否可执行其他操作
func makeLoading(title: String? = "", enable: Bool = false) {
// 加载图基本展示
var roundView: HsuBallRoatationView?
var needInitRoundView = true
var needInitCoverView = true
var needInitTitleView = true
subviews.forEach { (v) in
if v.tag == 10001 {
needInitRoundView = false
}
if v.tag == 10002 {
needInitCoverView = false
}
if v.tag == 10003 {
needInitTitleView = false
}
}
if needInitRoundView {
roundView = HsuBallRoatationView(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
}
guard let rView = roundView else {
return
}
rView.center = CGPoint(x: bounds.width / 2, y: bounds.height / 2)
rView.tag = 10001
addSubview(rView)
bringSubviewToFront(rView)
rView.backgroundColor = UIColor.clear
// 如果有文本
if title != nil && !title!.isEmpty && needInitTitleView {
let fontSize:CGFloat = 16
let strSize = (title! as NSString).boundingRect(with: CGSize(width: bounds.width - 50, height: 15), options: [], attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: fontSize)], context: nil).size
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: strSize.width > 100 ? strSize.width + 25 : 100, height: 80))
titleView.backgroundColor = UIColor.groupTableViewBackground
titleView.clipsToBounds = true
titleView.layer.cornerRadius = 10
titleView.center = CGPoint(x: bounds.width / 2, y: bounds.height / 2)
addSubview(titleView)
bringSubviewToFront(titleView)
titleView.tag = 10003
rView.removeFromSuperview()
rView.center = CGPoint(x: titleView.bounds.width / 2, y: 20)
titleView.addSubview(rView)
// 添加标题
let label = UILabel(frame: CGRect(x: 0, y: 0, width: strSize.width, height: 20))
label.text = title
label.font = UIFont.systemFont(ofSize: fontSize)
label.backgroundColor = UIColor.clear
label.textColor = UIColor.darkGray
label.textAlignment = .center
titleView.addSubview(label)
label.center = CGPoint(x: titleView.bounds.width / 2, y: titleView.bounds.height - 18)
}
// 如果不可点击,则在高层添加遮罩
if !enable {
if needInitCoverView {
let coverView = UIControl(frame: bounds)
coverView.isEnabled = true
coverView.tag = 10002
addSubview(coverView)
bringSubviewToFront(coverView)
}
}
}
/// 移除加载动画
func dismissLoading() {
subviews.forEach { (v) in
if v.tag == 10001 {
v.removeFromSuperview()
}
if v.tag == 10002 {
v.removeFromSuperview()
}
if v.tag == 10003 {
v.removeFromSuperview()
}
}
}
}
2、最终效果如下图
![](https://img.haomeiwen.com/i1910830/c4e4b724f4e2dd3b.gif)
网友评论