本文已参与「新人创作礼」活动,一起开启掘金创作之路
持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
- 播放按钮,需要根据播放的进度,在按钮上显示事实的进度
思考
- 进度条用贝塞尔曲线画一个圆,从0到2π 根据进度换算成对应的角度,然后layout button达到事实更新的效果
- 注意📢: 圆形的半径 要减去border的宽度,要不然圆环到按钮外面去了, layer.masktobound = no的时候完全看不见。上代码
#define RGBA(r, g, b, a) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:(a)]
#define DEGREES_TO_RADIANS(degrees) ((M_PI * (degrees))/ 180)
- (**void**)drawRect:(CGRect)rect{
CGSize size = **self**.bounds.size; // 视图的size
CGPoint arcCenter = CGPointMake(size.width/2, size.height/2); // 圆心坐标
CGFloat circleLineWidth = 4; // 圆线宽
CGFloat radius = size.width/2 - circleLineWidth/2; // 圆半径
CGFloat startRadian = 0; // 开始弧度
CGFloat progressDegree = 0; // 完成比的角度
CGFloat endRadian = 0; // 结束弧度
// 画大圆
startRadian = DEGREES_TO_RADIANS(-90);
progressDegree = 1 * 360;
endRadian = DEGREES_TO_RADIANS(progressDegree-90);
UIBezierPath *whileCircle = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:radius startAngle:startRadian endAngle:endRadian clockwise:**YES**];
[[UIColor clearColor] setStroke];
whileCircle.lineCapStyle = kCGLineCapRound;
whileCircle.lineWidth = circleLineWidth;
[whileCircle stroke];
// 画圆弧
startRadian = DEGREES_TO_RADIANS(-90);
progressDegree = **self**.progress * 360;
endRadian = DEGREES_TO_RADIANS(progressDegree-90);
UIBezierPath *arcCircle = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:radius startAngle:startRadian endAngle:endRadian clockwise:**YES**];
[RGBA(255, 59, 48, 1) setStroke];
arcCircle.lineCapStyle = kCGLineCapRound;
arcCircle.lineWidth = circleLineWidth;
[arcCircle stroke];
}
轻松搞定,然而,人算不如天算。圆环是画在按钮上的。button设置图片的时候,图片完全把圆环给遮住了
解决方案
- 没办法,那就把圆环画在imageView上,然后把imageview添加到button上。多了一层视图。但是能更简单的解决问题,还能防止后面产品更改其他的需求
-(**void**)layoutSubviews {
[**super** layoutSubviews];
CGFloat startRadian = DEGREES_TO_RADIANS(-90);
// progressDegree = self.progress * 360;
CGFloat endRadian = DEGREES_TO_RADIANS(0-90);
CGFloat width = **self**.width;
UIBezierPath *backgroundBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(width/2.0f, width/2.0f) radius:(CGRectGetWidth(**self**.bounds)- **self**.trackWidth)/2.f startAngle:startRadian endAngle:endRadian
clockwise:**YES**];
_backgroundLayer.path = backgroundBezierPath.CGPath;
//设置线宽
_frontFillLayer.lineWidth = **self**.trackWidth;
_backgroundLayer.lineWidth = **self**.trackWidth;
}
- (**void**)setProgress:(CGFloat)progress startAngle:(CGFloat )startAngle{
CGFloat endAngle = startAngle + (clockwise?(2*M_PI)*progress:(-2*M_PI)*progress);
UIBezierPath *frontFillBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(width/2.0f, width/2.0f) radius:(CGRectGetWidth(**self**.bounds)-**self**.trackWidth)/2.f startAngle:startAngle endAngle:endAngle clockwise:clockwise];
_frontFillLayer.path = frontFillBezierPath.CGPath;
}
新的问题来了
- button设置两种状态,nomal 和 selected 分别对应播放和暂停。暂停的时候进度条是不需要更新的。封装的原则就是让调用方尽可能简单的调用
解决
- 内部听select的状态,直接写select的set方法好像也拿不到_select成员,那就直接KVO监听select的变动
-(**void**)observeValueForKeyPath:(NSString *)keyPath ofObject:(**id**)object change:(NSDictionary<NSKeyValueChangeKey,**id**> *)change context:(**void** *)context{
**id** new = [change objectForKey:@"new"];
**BOOL** selected = [new boolValue];
**if** (selected) {
[**self** setSelectedType];
}**else**{
[**self** setDeselectedType];
}
}