这里我们实现一个 stroke and path animations 下拉刷新动画。最后效果如下:
pull-to-refresh animation1. 创建虚线圆
ovalShapeLayer.strokeColor = UIColor.white.cgColor
ovalShapeLayer.fillColor = UIColor.clear.cgColor
ovalShapeLayer.lineWidth = 4.0
// 虚线组成为2单位实线3单位虚线
ovalShapeLayer.lineDashPattern = [2, 3] // alternates between a 2-user-space-unit-long painted segment and a 3-user-space-unit-long unpainted segment
let refreshRadius = frame.size.height/2 * 0.8
ovalShapeLayer.path = UIBezierPath(ovalIn: CGRect(
x: frame.size.width/2 - refreshRadius,
y: frame.size.height/2 - refreshRadius,
width: 2 * refreshRadius,
height: 2 * refreshRadius)).cgPath
layer.addSublayer(ovalShapeLayer)
let airplaneImage = UIImage(named: "airplane.png")!
airplaneLayer.contents = airplaneImage.cgImage
airplaneLayer.bounds = CGRect(x: 0.0, y: 0.0,width: airplaneImage.size.width, height: airplaneImage.size.height)
airplaneLayer.position = CGPoint(
x: frame.size.width/2 + frame.size.height/2 * 0.8, y: frame.size.height/2)
layer.addSublayer(airplaneLayer)
airplaneLayer.opacity = 0.0
2. 实现 Path Animations
func beginRefreshing() {
isRefreshing = true
UIView.animate(withDuration: 0.3) {
var newInsets = self.scrollView.contentInset
newInsets.top += self.frame.size.height
self.scrollView.contentInset = newInsets
}
let strokeStartAnimation = CABasicAnimation(keyPath: "strokeStart")
strokeStartAnimation.fromValue = -0.5
strokeStartAnimation.toValue = 1.0
let strokeEndAnimation = CABasicAnimation(keyPath: "strokeEnd")
strokeEndAnimation.fromValue = 0.0
strokeEndAnimation.toValue = 1.0
let strokeAnimationGroup = CAAnimationGroup()
strokeAnimationGroup.duration = 1.5
strokeAnimationGroup.repeatDuration = 5.0
strokeAnimationGroup.animations = [strokeStartAnimation, strokeEndAnimation]
ovalShapeLayer.add(strokeAnimationGroup, forKey: nil)
let flightAnimation = CAKeyframeAnimation(keyPath: "position")
flightAnimation.path = ovalShapeLayer.path
flightAnimation.calculationMode = kCAAnimationPaced
let airplaneOrientationAnimation = CABasicAnimation(keyPath: "transform.rotation")
airplaneOrientationAnimation.fromValue = 0
airplaneOrientationAnimation.toValue = 2.0 * .pi
let flightAnimationGroup = CAAnimationGroup()
flightAnimationGroup.duration = 1.5
flightAnimationGroup.repeatDuration = 5.0
flightAnimationGroup.animations = [flightAnimation,airplaneOrientationAnimation]
airplaneLayer.add(flightAnimationGroup, forKey: nil)
}
func endRefreshing() {
isRefreshing = false
UIView.animate(withDuration: 0.3, delay:0.0, options: .curveEaseOut,
animations: {
var newInsets = self.scrollView.contentInset
newInsets.top -= self.frame.size.height
self.scrollView.contentInset = newInsets
},
completion: {_ in
//finished
}
)
}
func redrawFromProgress(_ progress: CGFloat) {
ovalShapeLayer.strokeEnd = progress
airplaneLayer.opacity = Float(progress)
}
3. strokeStart和strokeEnd 注释
可以把strokeStart理解成一个橡皮擦,如果我们让strokeStart从0到1的话 那么这个线就会被从(0,0)一直檫除到(100,0)
可以把strokeEnd理解为一个画笔,strokeEnd从0 动画到 1 那么动画表现为线越来越长。
eg. 对勾动画
先把路径用贝赛尔曲线画出来,然后用strokeEnd做动画:
#pragma mark -- 成功的路径
-(CGPathRef)getSuccessPath{
// 圆
UIBezierPath *ciclePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2,self.frame.size.height/2) radius:0.8*self.currentSize.width/2 startAngle:M_PI * 3 / 2 endAngle:M_PI * 7 / 2 clockwise:YES];
// 对勾
CGFloat W = self.frame.size.width *0.9;
CGFloat H = self.frame.size.height*0.9;
UIBezierPath *subPath = [UIBezierPath bezierPath];
[subPath moveToPoint:CGPointMake(W/4,H/2)];
[subPath addLineToPoint:CGPointMake(W/2,(H/2)+ H/4)];
[subPath addLineToPoint:CGPointMake(W-(W/8),H/4)];
// 添加到路径上 由于CAShapeLayer只能添加一条路径 幸好贝赛尔曲线有个appendPath: 方法 可以让我们画多条路径
[ciclePath appendPath:subPath];
return ciclePath.CGPath;
}
#pragma mark -- 成功的动画
- (CABasicAnimation*)successAnimtion{
// 第一个满圆旋转
CABasicAnimation *aniamtion1 = [CABasicAnimation animation];
aniamtion1.keyPath = @"strokeEnd";
aniamtion1.fromValue = @0;
aniamtion1.toValue = @1;
aniamtion1.duration = 1.5;
// 这个是缓冲函数
// 可以自定义
//
// aniamtion1.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1 :1 :1 :1];
aniamtion1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
aniamtion1.fillMode = kCAFillModeBackwards;
return aniamtion1;
}
参考:
感谢 @谢微一直都得踢足球
网友评论