文章简介
- 系统原生加载展示——UIActivityIndicatorView&UIProgressView
- 开源项目SVProgressHUD&MBProgressHUD
- 介绍 SVProgressHUD展示加载用到CAShapeLayer
- 结合CAShapeLayer中strokeStart和strokeEnd绘制动画
UIActivityIndicatorView
- (void)setUpActivityIndicators
{
_activityIndicatorView = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
//选择样式初始化菊花
/*
UIActivityIndicatorViewStyleWhiteLarge //大白
UIActivityIndicatorViewStyleWhite //小白
UIActivityIndicatorViewStyleGray //小灰
*/
//修改大小,使用变形属性
_activityIndicatorView.transform = CGAffineTransformMakeScale(2, 2);
//菊花的中心点
_activityIndicatorView.center = CGPointMake(kScreenWidth/2, kScreenHeight/2);
//转动菊花的颜色
_activityIndicatorView.color = [UIColor blackColor];
//停止转动时是否hidden
_activityIndicatorView.hidesWhenStopped = NO;
[self.view addSubview:_activityIndicatorView];
[_activityIndicatorView startAnimating];
//5s后停止转动
[self performSelector:@selector(stopActivityIndicator) withObject:nil afterDelay:5];
}
- (void)stopActivityIndicator
{
[_activityIndicatorView stopAnimating];
}
UIProgressView
- (void)setUpProgressView
{
_progressView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleBar];
//设置进度条的位置,高设置无用
_progressView.frame = CGRectMake(40, (kScreenHeight-20)/2, kScreenWidth-40*2, 20);
//改变进度条的高度
_progressView.transform = CGAffineTransformMakeScale(1.0f, 3.0);
//设置进度条走过的颜色or图片
_progressView.progressTintColor = [UIColor blueColor];
// _progressView.progressImage = [UIImage imageNamed:@"001.jpg"];
//设置进度条还未走到的颜色or图片
_progressView.trackTintColor = [UIColor grayColor];
// _progressView.trackImage = [UIImage imageNamed:@"002.jpg"];
[self.view addSubview: _progressView];
//设置进度条初始的进度
[_progressView setProgress:0.2 animated:YES];
[self performSelector:@selector(finishProgress) withObject:nil afterDelay:5];
}
- (void)finishProgress
{
[_progressView setProgress:1 animated:YES];
}
加载开源项目
SVProgressHUD
一开始在viewDidLoad
中执行[SVProgressHUD show]
,但是页面没有任何展示,查找原因,看到Still not working in viewDidLoad? 和Perform call to server in viewDidLoad;解决方法是
一种是移到viewDidAppear:
,
另一种是
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
});
}
[SVProgressHUD show];
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack];
[SVProgressHUD showWithStatus:@"waiting..."];
[SVProgressHUD showWithStatus:@"文件生成中..." maskType:SVProgressHUDMaskTypeGradient];
[SVProgressHUD showImage:[UIImage imageNamed:@"angle-mask.png"] status:@"已收藏"];
[SVProgressHUD showSuccessWithStatus:@"已保存" maskType:SVProgressHUDMaskTypeBlack];
[SVProgressHUD showErrorWithStatus:@"失败" maskType:SVProgressHUDMaskTypeBlack];
[SVProgressHUD dismiss];
具体实现
CAShapeLayer和mask结合实现小圆圈,maskLayer相关见CALayer属性mask
- (void)setUpMask
{
CGPoint arcCenter = CGPointMake(24, 24);
//利用贝塞尔曲线画圆
UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter
radius: 18
startAngle:M_PI*3/2
endAngle:M_PI/2+M_PI*5
clockwise:YES];
//通过CAShapeLayer绘制圆
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.contentsScale = [[UIScreen mainScreen] scale];
shapeLayer.frame = CGRectMake((kScreenWidth-arcCenter.x*2)/2, (kScreenHeight-arcCenter.x*2)/2, arcCenter.x*2, arcCenter.y*2);
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.lineJoin = kCALineJoinBevel;
shapeLayer.path = smoothedPath.CGPath;
//定义蒙版
CALayer *maskLayer = [CALayer layer];
NSString *imagePath = [[NSBundle mainBundle]pathForResource:@"angle-mask" ofType:@"png"];
maskLayer.contents = (__bridge id)[[UIImage imageWithContentsOfFile:imagePath] CGImage];
maskLayer.frame = shapeLayer.bounds;
//添加蒙版
shapeLayer.mask = maskLayer;
[self.view.layer addSublayer:shapeLayer];
}
因为SVProgressHUD需要有动画不停的旋转,这边是通过两个动画完成:旋转动画和一个动画组。
- 旋转动画
// 设置动画的延迟及类型
NSTimeInterval animationDuration = 1;
CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
// 注意value类型为id类型
animation.fromValue = (id) 0;
animation.toValue = @(M_PI*2);
animation.duration = animationDuration; animation.timingFunction = linearCurve;
// removedOnCompletion指动画之后保持动画完成的状态
animation.removedOnCompletion = NO;
animation.repeatCount = INFINITY;
animation.fillMode = kCAFillModeForwards;
animation.autoreverses = NO;
// 将动画加到mask上
[shapeLayer.mask addAnimation:animation forKey:@"rotate"];
- 动画组,会有一段小亮点在转动;涉及到strokeStart和strokeEnd,可以看下一节
// 创建动画组,并设置相关属性
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.duration = animationDuration;
animationGroup.repeatCount = INFINITY;
animationGroup.removedOnCompletion = NO;
animationGroup.timingFunction = linearCurve;
// strokeStart动画
CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
strokeStartAnimation.fromValue = @0.015;
strokeStartAnimation.toValue = @0.515;
// strokeEnd动画
CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
strokeEndAnimation.fromValue = @0.485;
strokeEndAnimation.toValue = @0.985;
// 将动画加到动画组
animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
[shapeLayer addAnimation:animationGroup forKey:@"progress"];
运行结果
strokeStart和strokeEnd动画
做了个测试,一个小红圈分别设置strokeStart和strokeEnd,运行会发现
- (void)testStroke
{
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGPoint arcCenter = CGPointMake(24, 24);
shapeLayer.frame = CGRectMake((kScreenWidth-arcCenter.x*2)/2, (kScreenHeight-arcCenter.x*2)/2, arcCenter.x*2, arcCenter.y*2);;
UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:arcCenter
radius: 18
startAngle:M_PI*3/2
endAngle:M_PI/2+M_PI*5
clockwise:YES];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0f;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
CABasicAnimation *pathAnima = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
pathAnima.duration = 3.0f;
pathAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnima.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnima.toValue = [NSNumber numberWithFloat:1.0f];
pathAnima.fillMode = kCAFillModeForwards;
pathAnima.removedOnCompletion = NO;
[shapeLayer addAnimation:pathAnima forKey:@"strokeStartAnimation"];
// CABasicAnimation *pathAnima = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
// pathAnima.duration = 3.0f;
// pathAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// pathAnima.fromValue = [NSNumber numberWithFloat:0.0f];
// pathAnima.toValue = [NSNumber numberWithFloat:1.0f];
// pathAnima.fillMode = kCAFillModeForwards;
// pathAnima.removedOnCompletion = NO;
// [shapeLayer addAnimation:pathAnima forKey:@"strokeEndAnimation"];
}
动画属性 | fromValue | toValue | result |
---|---|---|---|
strokeStart | 0 | 1 | 圆圈顺时针消失 |
strokeStart | 1 | 0 | 圆圈逆时针出现 |
strokeEnd | 0 | 1 | 圆圈顺时针出现 |
strokeEnd | 1 | 0 | 圆圈逆时针消失 |
结果截图,依照顺序
很好奇为什么是这样的结果,看到iOS动画学习之CAShapeLayer使用
中总结,
- 对strokeStart应用动画时:
- fromValue<toValue,则实际上是在消除从0到toValue这部分的绘图,但是只有fromValue到toValue这部分会有动画,0-fromValue这部分会立即消除,而toValue到1这部分不会改变
- fromValue>toValue,则实际上是在画从fromValue到toValue这部分的绘图,但是只有fromValue到toValue这部分会有动画,1到fromValue这部分不会改变,而toValue到0这部分会立即消除
- 总结而言:max(fromValue,toValue)到1这一段绘图不会改变,而0到min(fromValue,toValue)这一段绘图会立即消除
- 对strokeEnd应用动画时:
- fromValue<toValue则实际上是在画从fromValue到toValue这部分的绘图,但是只有fromValue到toValue这部分会有动画,1到fromValue这部分不会改变
- fromValue>toValue,则实际上是在消除从1到toValue这部分的绘图,但是只有fromValue到toValue这部分会有动画,1到fromValue这部分会立即消除
- 感谢 文/ThanWork(简书作者)
原文链接:http://www.jianshu.com/p/5c875f4b80d6
MBProgressHUD
- (IBAction)clickTextWithLoadingBtn:(id)sender {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
//将当前的view置于最后
hud.dimBackground = YES;
//设置加载中的文字
hud.labelText = @"加载中...";
//5s之后消失
[hud hide:YES afterDelay:5];
}
- (IBAction)clickProgressOneBtn:(id)sender {
MBProgressHUD *hud = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview: hud];
hud.labelText = @"加载中...";
hud.mode = MBProgressHUDModeDeterminate;
/*
typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
// Progress is shown using an UIActivityIndicatorView. This is the default.
MBProgressHUDModeIndeterminate,
// Progress is shown using a round, pie-chart like, progress view.
MBProgressHUDModeDeterminate,
// Progress is shown using a horizontal progress bar
MBProgressHUDModeDeterminateHorizontalBar,
// Progress is shown using a ring-shaped progress view.
MBProgressHUDModeAnnularDeterminate,
// Shows a custom view
MBProgressHUDModeCustomView,
// Shows only labels
MBProgressHUDModeText
};
*/
[hud showAnimated:YES
whileExecutingBlock:^{
float progress = 0.0;
while (progress < 1.0) {
progress += 0.1;
hud.progress = progress;
usleep(500000);
}}
completionBlock:^{
[hud removeFromSuperview];
}];
}
- (IBAction)clickProgressTwoBtn:(id)sender {
MBProgressHUD *hud = [[MBProgressHUD alloc]initWithView:self.view];
[self.view addSubview:hud];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.labelText = @"加载中";
[hud showAnimated:YES whileExecutingBlock:^{
float progress = 0.0;
while (progress < 1.0) {
progress += 0.1;
hud.progress = progress;
usleep(50000);
}
} completionBlock:^{
[hud removeFromSuperview];
}];
}
- (IBAction)clickCustomBtn:(id)sender {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = @"添加到喜欢";
hud.mode = MBProgressHUDModeCustomView;
hud.customView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"love.png"]];
[hud hide:YES afterDelay:2];
}
- (IBAction)clickTextBtn:(id)sender {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = @"已添加";
hud.mode = MBProgressHUDModeText;
//距离中心点的偏移
hud.xOffset = 100;
hud.yOffset = 150;
[hud hide:YES afterDelay:2];
}
运行结果
参考
专有图层
CAShapeLayer和CAGradientLayer
UIBezierPath精讲
MBProgressHUD实现分析