渐变直线/圆形进度条实现
2017-12-05 本文已影响0人
Junetaurus
ProgressBar.gif
然后为
直线进度条
-
结构
第一部分:背景 view
第二部分:填充 view,用 CAGradientLayer 填充 - 核心代码
/*
CAGradientLayer 渐变色,目前只有 kCAGradientLayerAxial 一个选项,即呈线性变化
startPoint、endPoint:分别表示渐变层的起始位置和终止位置,这两个点被定义在一个单元坐标空间,[0,0] 表示左上角位置,[1,1] 表示右下角位置,默认值分别是 [0.5,0] and [0.5,1]
colors:颜色数组,定义渐变层的各个颜色
locations: 可选的 NSNumber 数组,决定每个渐变颜色的终止位置,这些值必须是递增的,数组的长度和 colors 的长度最好一致
*/
- (void)gradientColorForView:(UIView *)view {
CAGradientLayer *layer = [CAGradientLayer layer];
layer.startPoint = CGPointMake(0, 0);
layer.endPoint = CGPointMake(1, 0);
layer.colors = [NSArray arrayWithObjects:(__bridge id)[UIColor colorWithRed:24/255.0 green:201/255.0 blue:255/255.0 alpha:1].CGColor,(__bridge id)[UIColor colorWithRed:0/255.0 green:147/255.0 blue:255/255.0 alpha:1].CGColor, nil];
layer.locations = @[@0,@1];
[view.layer insertSublayer:layer atIndex:0];
}
圆形进度条
- 首先画个圆,
这里就需要用到UIBezierPath
和CAShapeLayer
了。
@property (nonatomic, strong) UIBezierPath *bezierPath;
@property (nonatomic, strong) CAShapeLayer *inCAShapeLayer;
self.bezierPath = [UIBezierPath bezierPath];
self.inCAShapeLayer = [CAShapeLayer layer];
self.inCAShapeLayer.lineWidth = self.lineWidth/2;
self.inCAShapeLayer.strokeColor = [UIColor colorWithRed:219/255.0 green:219/255.0 blue:219/255.0 alpha:1].CGColor;
self.inCAShapeLayer.fillColor = [UIColor whiteColor].CGColor;
self.inCAShapeLayer.lineCap = kCALineCapRound
[self.layer addSublayer:self.inCAShapeLayer];
[self.bezierPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2) radius:self.bounds.size.width/2 - self.lineWidth startAngle:0 endAngle:M_PI*2 clockwise:YES]];
self.inCAShapeLayer.path = self.bezierPath.CGPath;
效果图
CAShapeLayer
是 CALayer 的子类,比 CALayer 更灵活,可以画出各种图形。fillColor
代表这个 Layer 的填充色,strokeColor
代表这个 Layer 的边框色。UIBezierPath
贝塞尔曲线,可以用来构建一段弧线,你可以用任意条弧线来组成你想要的形状。
- 渐变填充色
渐变色就需要上面用到过的CAShapeLayer
了。
先添加左边的:
@property (nonatomic, strong) CALayer *caLayer;
@property (nonatomic, strong) CAGradientLayer *leftGradientLayer;
self.caLayer = [CALayer layer];
[self.layer addSublayer:self.caLayer];
self.leftGradientLayer = [CAGradientLayer layer];
self.leftGradientLayer.startPoint = CGPointMake(0, 1);
self.leftGradientLayer.endPoint = CGPointMake(0, 0);
self.leftGradientLayer.colors = [NSArray arrayWithObjects:(__bridge id)[UIColor colorWithRed:237/255.0 green:199/255.0 blue:109/255.0 alpha:1].CGColor,(__bridge id)[UIColor colorWithRed:219.5/255.0 green:181/255.0 blue:100.5/255.0 alpha:1].CGColor, nil];
[self.caLayer addSublayer:self.leftGradientLayer];
效果图
在添加右边的:
@property (nonatomic, strong) CAGradientLayer *rightGradientLayer;
self.rightGradientLayer = [CAGradientLayer layer];
self.rightGradientLayer.startPoint = CGPointMake(0, 0);
self.rightGradientLayer.endPoint = CGPointMake(0, 1);
self.rightGradientLayer.colors = [NSArray arrayWithObjects:(__bridge id)[UIColor colorWithRed:219.5/255.0 green:181/255.0 blue:100.5/255.0 alpha:1].CGColor,(__bridge id)[UIColor colorWithRed:202/255.0 green:163/255.0 blue:92/255.0 alpha:1].CGColor, nil];
[self.caLayer addSublayer:self.rightGradientLayer];
效果图
然后为
self.caLayer``setMask
设置一个遮罩,遮罩范围外面的就不会在显示了。
@property (nonatomic, strong) CAShapeLayer *outCAShapeLayer;
self.outCAShapeLayer = [CAShapeLayer layer];
self.outCAShapeLayer.lineWidth = self.lineWidth;
self.outCAShapeLayer.fillColor = [UIColor clearColor].CGColor;
self.outCAShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
self.outCAShapeLayer.lineCap = kCALineCapRound;
[self.caLayer setMask:self.outCAShapeLayer];
[self.bezierPath appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2) radius:self.bounds.size.width/2 - self.lineWidth startAngle:self.startAngle endAngle:self.endAngle clockwise:YES]];
self.outCAShapeLayer.path = self.bezierPath.CGPath;
效果图
- 添加动画
@property (nonatomic, strong) CABasicAnimation *caAnimation;
- (CABasicAnimation *)caAnimation {
if (!_caAnimation) {
_caAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_caAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
_caAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
_caAnimation.toValue = [NSNumber numberWithFloat:1.0f];
}
return _caAnimation;
}
- (void)setProgress:(CGFloat)progressValue withValueMax:(CGFloat)progressMaxValue {
if (progressValue > progressMaxValue) progressValue = progressMaxValue;
CGFloat allAngle = (progressValue/progressMaxValue) * M_PI * 2;
//弧长L=αr 其中 α 为圆心角的弧度数,r 为圆的半径 α = L/r
self.startAngle = M_PI_2 + (self.lineWidth/2)/(self.bounds.size.width/2 - self.lineWidth);
self.endAngle = allAngle + self.startAngle;
[self.outCAShapeLayer removeAllAnimations];
if (progressMaxValue) {
self.caAnimation.duration = (progressValue/progressMaxValue)*3.0;
//添加动画
[self.outCAShapeLayer addAnimation:self.caAnimation forKey:@"strokeEndAnimation"];
}
/*
-setNeedsLayout 方法:标记为需要重新布局,异步调用 layoutIfNeeded 刷新布局,不立即刷新,但 layoutSubviews 一定会被调用
-layoutIfNeeded 方法:如果,有需要刷新的标记,立即调用 layoutSubviews 进行布局(如果没有标记,不会调用 layoutSubviews)
如果要立即刷新,要先调用 [view setNeedsLayout],把标记设为需要布局,然后马上调用 [view layoutIfNeeded],实现布局。
*/
[self setNeedsLayout];
}
完整的Demo下载。