愛奇異更新版本之後,那個loading效果比較簡潔,卻又不失美觀,遂仿製一把,以供廣大程序員交流學習,效果圖如下,
效果圖中是垂直效果,但只要在路徑那裏改一下即可,還有一個縮放動畫未添加,留給看官們自己處理
動畫分析:
圓弧動畫:UIBezierPath +CAShapeLayer
其中UIBezierPath畫出左右2邊的圓弧,然後用基本動畫的storkEnd或storkStart來驅動即可,達到動畫效果
代碼如下:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 0.7f;
animation.fromValue = @(1);
animation.toValue = @(0.0);
//animation.delegate = self;
//animation.repeatCount = MAXFLOAT;
// 保持動畫結束時的狀態
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animation setValue:@"arcRotateAnim" forKey:@"animationName"];
[self.leftArcLayer addAnimation:animation forKey:@""];
[self.rightArcLayer addAnimation:animation forKey:@""];
//畫布旋轉動畫
CABasicAnimation *BasicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
BasicAnimation.toValue=@(M_PI*2);
BasicAnimation.duration= 0.7;
//BasicAnimation.delegate=self;
[BasicAnimation setValue:@"BasicAnimationRotation" forKey:@"animationName"];
[self.layer removeAllAnimations];
[self.layer addAnimation:BasicAnimation forKey:@"BasicAnimationRotation"];
圖層分析 :CAShapeLayer在強大之處這裏不細說,本案例中主要有4個圖層 ,分別使用懶加載實現,代碼如下:
/**
重寫get屬性
@return CAShapeLayer
*/
-(CAShapeLayer*) leftArcLayer{
if (!_leftArcLayer) {
_leftArcLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:center radius:25 startAngle: -PI / 2 endAngle:PI / 2 clockwise:YES];
_leftArcLayer.borderColor = [UIColor blackColor].CGColor;
_leftArcLayer.lineWidth = 1.f;
_leftArcLayer.path = path.CGPath;
_leftArcLayer.fillColor = [UIColor clearColor].CGColor;
_leftArcLayer.strokeColor = [UIColor greenColor].CGColor;
}
return _leftArcLayer;
}
-(CAShapeLayer*) rightArcLayer{
if (!_rightArcLayer) {
_rightArcLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:center radius:25 startAngle: PI / 2 endAngle:-PI / 2 clockwise:YES];
_rightArcLayer.borderColor = [UIColor blackColor].CGColor;
_rightArcLayer.lineWidth = 1.f;
_rightArcLayer.path = path.CGPath;
_rightArcLayer.fillColor = [UIColor clearColor].CGColor;
_rightArcLayer.strokeColor = [UIColor greenColor].CGColor;
}
return _rightArcLayer;
}
-(CAShapeLayer*) leftPointLayer{
if (!_leftPointLayer) {
_leftPointLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
_leftPointLayer.bounds = CGRectMake(0, 0, 2, 2);
_leftPointLayer.position = center;
_leftPointLayer.backgroundColor = [UIColor greenColor].CGColor;
}
return _leftPointLayer;
}
-(CAShapeLayer*) rightPointLayer{
if (!_rightPointLayer) {
_rightPointLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
_rightPointLayer.bounds = CGRectMake(0, 0, 2, 2);
_rightPointLayer.position = center;
_rightPointLayer.backgroundColor = [UIColor greenColor].CGColor;
}
return _rightPointLayer;
}
圓點動畫:基本動畫中的位移動畫:代碼如下:
-(void) middlePointMoveAnim{
CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"position"];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
moveAnim.fromValue = [NSValue valueWithCGPoint:center];
moveAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y - 25)];
moveAnim.duration = 0.5f;
moveAnim.delegate = self;
moveAnim.fillMode = kCAFillModeForwards;
moveAnim.removedOnCompletion = NO;
moveAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//[self.leftPointLayer removeAllAnimations];
[moveAnim setValue:@"positionAnimation" forKey:@"moveAnim"];
[self.leftPointLayer addAnimation:moveAnim forKey:@""];
CABasicAnimation *moveRightAnim = [CABasicAnimation animationWithKeyPath:@"position"];
moveRightAnim.fromValue = [NSValue valueWithCGPoint:center];
moveRightAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y + 25)];
moveRightAnim.duration = 0.5f;
//moveRightAnim.delegate = self;
moveRightAnim.fillMode = kCAFillModeForwards;
moveRightAnim.removedOnCompletion = NO;
moveRightAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//[self.rightPointLayer removeAllAnimations];
[self.rightPointLayer addAnimation:moveRightAnim forKey:@""];
}
全部代碼如下:
.h文件
@interface ShapeLayer_BezierParh : UIView<CAAnimationDelegate>
@property(nonatomic,strong) CAShapeLayer* leftArcLayer;
@property(nonatomic,strong) CAShapeLayer* rightArcLayer;
@property(nonatomic,strong) CAShapeLayer* leftPointLayer;
@property(nonatomic,strong) CAShapeLayer* rightPointLayer;
-(void) startAnim;
-(void) middlePointMoveAnim;
@end
.m文件:
#import "ShapeLayer_BezierParh.h"
#define PI 3.1415926
@implementation ShapeLayer_BezierParh
-(instancetype) initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
[self.layer addSublayer:self.leftArcLayer];
[self.layer addSublayer:self.rightArcLayer];
[self.layer addSublayer:self.leftPointLayer];
[self.layer addSublayer:self.rightPointLayer];
}
return self;
}
/**
重寫get屬性
@return CAShapeLayer
*/
-(CAShapeLayer*) leftArcLayer{
if (!_leftArcLayer) {
_leftArcLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:center radius:25 startAngle: -PI / 2 endAngle:PI / 2 clockwise:YES];
_leftArcLayer.borderColor = [UIColor blackColor].CGColor;
_leftArcLayer.lineWidth = 1.f;
_leftArcLayer.path = path.CGPath;
_leftArcLayer.fillColor = [UIColor clearColor].CGColor;
_leftArcLayer.strokeColor = [UIColor greenColor].CGColor;
}
return _leftArcLayer;
}
-(CAShapeLayer*) rightArcLayer{
if (!_rightArcLayer) {
_rightArcLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
UIBezierPath *path = [UIBezierPath bezierPath];
[path addArcWithCenter:center radius:25 startAngle: PI / 2 endAngle:-PI / 2 clockwise:YES];
_rightArcLayer.borderColor = [UIColor blackColor].CGColor;
_rightArcLayer.lineWidth = 1.f;
_rightArcLayer.path = path.CGPath;
_rightArcLayer.fillColor = [UIColor clearColor].CGColor;
_rightArcLayer.strokeColor = [UIColor greenColor].CGColor;
}
return _rightArcLayer;
}
-(CAShapeLayer*) leftPointLayer{
if (!_leftPointLayer) {
_leftPointLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
_leftPointLayer.bounds = CGRectMake(0, 0, 2, 2);
_leftPointLayer.position = center;
_leftPointLayer.backgroundColor = [UIColor greenColor].CGColor;
}
return _leftPointLayer;
}
-(CAShapeLayer*) rightPointLayer{
if (!_rightPointLayer) {
_rightPointLayer = [CAShapeLayer layer];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
_rightPointLayer.bounds = CGRectMake(0, 0, 2, 2);
_rightPointLayer.position = center;
_rightPointLayer.backgroundColor = [UIColor greenColor].CGColor;
}
return _rightPointLayer;
}
-(void) startAnim{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 0.7f;
animation.fromValue = @(1);
animation.toValue = @(0.0);
//animation.delegate = self;
//animation.repeatCount = MAXFLOAT;
// 保持動畫結束時的狀態
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animation setValue:@"arcRotateAnim" forKey:@"animationName"];
[self.leftArcLayer addAnimation:animation forKey:@""];
[self.rightArcLayer addAnimation:animation forKey:@""];
//畫布旋轉動畫
CABasicAnimation *BasicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
BasicAnimation.toValue=@(M_PI*2);
BasicAnimation.duration= 0.7;
//BasicAnimation.delegate=self;
[BasicAnimation setValue:@"BasicAnimationRotation" forKey:@"animationName"];
[self.layer removeAllAnimations];
[self.layer addAnimation:BasicAnimation forKey:@"BasicAnimationRotation"];
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[self middlePointMoveAnim];
});
}
-(void) middlePointMoveAnim{
CABasicAnimation *moveAnim = [CABasicAnimation animationWithKeyPath:@"position"];
CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2);
moveAnim.fromValue = [NSValue valueWithCGPoint:center];
moveAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y - 25)];
moveAnim.duration = 0.5f;
moveAnim.delegate = self;
moveAnim.fillMode = kCAFillModeForwards;
moveAnim.removedOnCompletion = NO;
moveAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//[self.leftPointLayer removeAllAnimations];
[moveAnim setValue:@"positionAnimation" forKey:@"moveAnim"];
[self.leftPointLayer addAnimation:moveAnim forKey:@""];
CABasicAnimation *moveRightAnim = [CABasicAnimation animationWithKeyPath:@"position"];
moveRightAnim.fromValue = [NSValue valueWithCGPoint:center];
moveRightAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(center.x , center.y + 25)];
moveRightAnim.duration = 0.5f;
//moveRightAnim.delegate = self;
moveRightAnim.fillMode = kCAFillModeForwards;
moveRightAnim.removedOnCompletion = NO;
moveRightAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//[self.rightPointLayer removeAllAnimations];
[self.rightPointLayer addAnimation:moveRightAnim forKey:@""];
}
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if([[anim valueForKey:@"animationName"] isEqualToString:@"arcRotateAnim"]){
NSLog(@"144----------------arcRotateAnim:stop %d",flag);
if(flag){
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[self middlePointMoveAnim];
});
}
}
else if([[anim valueForKey:@"moveAnim"] isEqualToString:@"positionAnimation"]){
NSLog(@"171----------------moveAnim:stop :%d",flag);
if(flag){
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[self startAnim];
});
}
}
}
@end
在vc中的測試代碼如下:
-(void) testShapeLayer{
self.bezierParhLayer = [[ShapeLayer_BezierParh alloc] initWithFrame:CGRectMake((CGRectGetWidth(self.view.frame) / 2) - 25, 100, 100, 100)];
//self.bezierParhLayer.backgroundColor = [UIColor wh];
[self.view addSubview:self.bezierParhLayer];
}
將開啓動畫放在一個按鈕下,點擊按鈕進行動畫啓動:
[self.bezierParhLayer middlePointMoveAnim];
其中屬性:
@property(nonatomic,strong) ShapeLayer_BezierParh* bezierParhLayer;