// // HDLwaveSeekBar.m // HDL_Widget_iOS // // Created by HDL on 2019/8/27. // Copyright © 2019 JLChen. All rights reserved. // #import "HDLWaveSeekBar.h" #import "HDLUtlisXM.h" #define APP_DEFAULT_BG [UIColor colorWithRed:72/255.0f green:72/255.0f blue:73/255.0f alpha:1.0f] #define APP_DEFAULT_BG_LINE [UIColor colorWithRed:73/255.0f green:87/255.0f blue:128/255.0f alpha:1.0f] //#define APP_DEFAULT_BAR [UIColor colorWithRed:252/255.0f green:59/255.0f blue:62/255.0f alpha:0.8f] #define DEFAULT_WAVE_COLOR HEXCOLOR(0xFD692E) #define DEFAULT_WAVE_END_COLOR HEXCOLOR(0xFB3140) #define ToRad(deg) ( (M_PI * (deg)) / 180.0 ) #define ToDeg(rad) ( (180.0 * (rad)) / M_PI ) #define SQR(x) ( (x) * (x) ) #define GETEndAngle(X) ( ((90-X) < 0) ? ((90-X)+360) : (90-X) ) #define GETStartAngle(X) ( 90 + X) //#define DEFAULT_DISTANCE_BETWEEN_TEXTPOINT_AND_ARC 30 // 进度显示文字坐标与进度圆弧的距离 #define DEFAULT_PADDING 5 //圆弧自带PADDING值,为了显示进度test文字 #define DEFAULT_OPEN_ANGLE 30.0f // 开口角度 @interface HDLWaveSeekbar () /** 是否正在移动 */ @property (nonatomic, assign) BOOL bTouchMove; /** 进度单位符号 */ @property (nonatomic, strong) NSString *mProgressBarUnitSring; /** 最小值 */ @property (nonatomic, assign) float mMinValue; /** 最大值 */ @property (nonatomic, assign) float mMaxValue; /** 进度渐变颜色数组 */ @property (nonatomic, strong) NSArray *mWaveProgressBarColors; /** 背景线宽 */ @property (nonatomic, assign) int mBackLineWidth; /** 边距 */ @property (nonatomic, assign) int WavePadding; @end @implementation HDLWaveSeekbar{ int _mMoveCount; CGPoint _mCenterPoint; //圆心坐标 bool _bIsInWaveProgress; CGFloat _mWaveHeight; CGFloat _mWaveWidth; CGFloat _mWaveStartX; CGFloat _mWaveEndX; CGFloat _waveA; // A CGFloat _waveW; // ω // CGFloat _offsetF; // φ firstLayer // CGFloat _offsetS; // φ secondLayer CGFloat _currentK; // k float _mPercent; CGFloat _mWavePadding; CGFloat _mCurtainY; } -(instancetype)initWithFrame:(CGRect)frame{ self = [super initWithFrame:frame]; if (self) { _isClickable = YES; _mMoveCount = 0; _mProgress = 0; _mPercent = 0; _mMaxValue = 100.0f; _mMinValue = 0.0f; _mBackLineWidth = 2; _mCornerRadius = 10; _WavePadding = 10; _mProgressTextColor = [UIColor blackColor]; _mProgressTextSize = 15; _mProgressBarUnitSring = @"%"; _mWaveBorderColor = APP_DEFAULT_BG_LINE; _isProgressTextShow = YES; // _mWaveProgressBarColor = APP_DEFAULT_BAR; _waveA = 6.0f; // [self setProgressBarColor:APP_DEFAULT_BAR]; _mWaveProgressBarColors = @[ (id) DEFAULT_WAVE_COLOR.CGColor, (id)DEFAULT_WAVE_END_COLOR.CGColor ]; [self refreshFrame]; self.backgroundColor = [UIColor clearColor]; } return self; } /** 刷新布局,更新关键值 */ -(void)refreshFrame{ _mCenterPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); _mWavePadding = _WavePadding*2 + _mBackLineWidth; _mWaveHeight = self.frame.size.height - _mWavePadding*2; _mWaveWidth = self.frame.size.width - _mWavePadding*2; _mWaveStartX = _mWavePadding; _mWaveEndX = _mWaveWidth + _mWaveStartX; } #pragma mark drawRect 绘制图形 -(void)drawRect:(CGRect)rect{ [super drawRect:rect]; CGContextRef ctx = UIGraphicsGetCurrentContext(); //将当前图形状态推入堆栈 CGContextSaveGState(ctx); //*********绘制边框背景********* [self drawBorderPath:ctx]; //*********绘制动态的进度条********* [self drawProgressBar:ctx]; //*********绘制显示进度值文字********* [self drawProgressText:ctx]; // [self drawArcWithGradient:ctx rect:rect];//绘制渐变效果 //把堆栈顶部的状态弹出 CGContextRestoreGState(ctx); } /** 绘制边框 @param ctx 画布 */ -(void)drawBorderPath:(CGContextRef)ctx{ CGRect drawRect = {_WavePadding+_mBackLineWidth/2, _WavePadding+_mBackLineWidth/2, self.frame.size.width-_WavePadding*2-_mBackLineWidth, self.frame.size.height-_WavePadding*2-_mBackLineWidth}; UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:drawRect cornerRadius:_mCornerRadius]; path.lineWidth = _mBackLineWidth; //线宽 path.lineCapStyle = kCGLineCapRound; //弯角样式 path.lineJoinStyle = kCGLineJoinBevel; //交点的样式 [_mWaveBorderColor set]; CGContextSetLineWidth(ctx, _mBackLineWidth); CGContextAddPath(ctx, path.CGPath); CGContextDrawPath(ctx, kCGPathStroke); // [path stroke]; //边框线颜色 } /* y = Asin(ωx+φ)+k A表示振幅,使用这个变量来调整波浪的高度 ω表示频率,使用这个变量来调整波浪密集度 φ表示初相,使用这个变量来调整波浪初始位置 k表示高度,使用这个变量来调整波浪在屏幕中y轴的位置。 */ /** 绘制进度条 @param ctx 画布 */ -(void)drawProgressBar:(CGContextRef)ctx{ //*******绘制剪裁限制显示区域********* CGRect drawRect2 = CGRectMake(_mWaveStartX, _mWavePadding , _mWaveWidth, _mWaveHeight ); UIBezierPath *path2 = [UIBezierPath bezierPathWithRoundedRect:drawRect2 cornerRadius:_mCornerRadius];; CGContextAddPath(ctx, path2.CGPath); // 剪裁路径 CGContextClip(ctx); //*******绘制剪裁限制显示区域********* CGFloat y = _mWaveHeight - _mPercent * _mWaveHeight + _mWavePadding; _mCurtainY = y; CGContextBeginPath( ctx ); //1、添加第一点 CGContextMoveToPoint( ctx, _mWaveStartX, y ); CGFloat h = y; if(_mPercent>0 && _mPercent<1){ //2、添加水波浪效果,正玄函数 来计算每一个像素的坐标 for (int i = _mWaveStartX; i <= _mWaveEndX; ++i) { y= h + _waveA * sin((i+h)*M_PI/60); CGContextAddLineToPoint(ctx, i, y); } CGContextAddLineToPoint(ctx, _mWaveEndX, _mWavePadding + _mWaveHeight); CGContextAddLineToPoint(ctx, _mWaveStartX, _mWavePadding + _mWaveHeight); }else{ //起点-终点 CGPoint pointsRect[4] = {CGPointMake(_mWaveStartX, h),CGPointMake(_mWaveStartX, _mWavePadding + _mWaveHeight), CGPointMake(_mWaveEndX, _mWavePadding + _mWaveHeight),CGPointMake(_mWaveEndX, h)}; CGContextAddLines(ctx, pointsRect, 4); } [self drawArcWithGradient:ctx rect:drawRect2]; // CGContextClosePath(ctx); // // CGContextSetFillColorWithColor( ctx, _mWaveProgressBarColor.CGColor ); // //// CGContextSetStrokeColorWithColor( ctx, APP_DEFAULT_BAR.CGColor ); // //// CGContextSetLineWidth( ctx, 1 ); // // CGContextDrawPath( ctx, kCGPathFill ); } /** 绘制进度显示值文字 @param ctx 画布 */ -(void)drawProgressText:(CGContextRef)ctx{ if(_isProgressTextShow){ if(_mPercent > 0.6){ _mProgressTextColor = [UIColor whiteColor]; }else{ _mProgressTextColor = [UIColor blackColor]; } [self drawString:[NSString stringWithFormat:@"%d%@", _mProgress, _mProgressBarUnitSring] outsidePoint:_mCenterPoint]; } } /** drawString 现在当前进度值 @param mText 显示文本 @param outsidePoint 坐标 */ - (void) drawString:(NSString *)mText outsidePoint:(CGPoint)outsidePoint{ NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init]; paragraph.alignment = NSTextAlignmentCenter; NSDictionary *dic = @{NSFontAttributeName : [UIFont systemFontOfSize:_mProgressTextSize], NSForegroundColorAttributeName : _mProgressTextColor, NSParagraphStyleAttributeName : paragraph }; // CGRect textRect = CGRectMake(outsidePoint.x, outsidePoint.y, 80, 20); // [mText drawInRect:textRect withAttributes:dic]; //2019-08-15 修改文字绘制方法,最终实现文本居中效果 CGSize textSize = [mText sizeWithAttributes:dic]; CGPoint textPoint = CGPointMake(outsidePoint.x - textSize.width/2, outsidePoint.y - textSize.height/2);//根据中点坐标绘制 [mText drawAtPoint:textPoint withAttributes:dic]; } /** 绘制渐变效果 */ -(void)drawArcWithGradient:(CGContextRef)ctx rect:(CGRect)rect{ // 创建一个渐变色 // 创建RGB色彩空间,创建这个以后,context里面用的颜色都是用RGB表示 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)_mWaveProgressBarColors, NULL); // 释放色彩空间 CGColorSpaceRelease(colorSpace); colorSpace = NULL; // 剪裁路径 CGContextClip(ctx); // 用渐变色填充 CGContextDrawLinearGradient(ctx, gradient, CGPointMake(rect.origin.x, rect.origin.y), CGPointMake(rect.origin.x+rect.size.width, rect.origin.y+rect.size.height), 0); // 释放渐变色 CGGradientRelease(gradient); gradient = NULL; CGContextRestoreGState(ctx);// 恢复到之前的context CGContextSaveGState(ctx); } ///** // 计算2点坐标之间的距离 // // @param startPoint 起点坐标 // @param endPonit 终点坐标 // @return 距离 // */ //-(float) getTwoPointDistance:(CGPoint)startPoint endPonit:(CGPoint)endPonit{ // CGPoint v = CGPointMake(startPoint.x - endPonit.x, startPoint.y - endPonit.y); // float d = sqrt(SQR(v.x) + SQR(v.y)); // NSLog(@"2点距离:%f ", d); // return d; // //} #pragma mark Touch Event 点击事件 -(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { [super beginTrackingWithTouch:touch withEvent:event]; if(!_isClickable) return YES;//禁止点击 _bTouchMove = NO; _mMoveCount = 0; // CGPoint startPoint = [touch locationInView:self]; // _bIsInArcProgress = [self getStartPointIsInArcProgress:startPoint];//判断开始坐标是否在可点击区域 _bIsInWaveProgress = YES;//不限制点击区域 [self.mProgressChangedDelegate onStartTrackingTouch]; return YES; } -(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event{ [super endTrackingWithTouch:touch withEvent:event]; if(!_isClickable) return;//禁止点击 if(!_bTouchMove){//没移动,仅点击 CGPoint lastPoint = [touch locationInView:self]; [self getCurrentProgressWithLastPoint:lastPoint]; [self sendActionsForControlEvents:UIControlEventValueChanged]; } [self.mProgressChangedDelegate onStopTrackingTouch:_mProgress]; } -(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { [super continueTrackingWithTouch:touch withEvent:event]; if(!_isClickable) return YES;//禁止点击 if(_mMoveCount < 2){ _mMoveCount++; }else{ _bTouchMove = YES; //开始移动 } if(_bIsInWaveProgress){//如果刚开始点击的坐标在可点击区域 CGPoint lastPoint = [touch locationInView:self]; [self getCurrentProgressWithLastPoint:lastPoint]; } [self sendActionsForControlEvents:UIControlEventValueChanged]; return YES; } ///** // 判断最后离开坐标是否在可点击区域 // // @param lastPoint 最后的坐标 // */ //-(void)getEndPointIsInArcProgress:(CGPoint)lastPoint { // [self getCurrentProgressWithLastPoint:lastPoint]; //} /** 根据坐标计算出当前进度百分比和对应的进度值 @param point 当前坐标 */ -(void)getCurrentProgressWithLastPoint:(CGPoint)point { _mPercent = (point.y - 10 - _mBackLineWidth ) / _mWaveHeight; // NSLog(@"point.y:%f _mWaveHeight:%f",point.y,_mWaveHeight); // NSLog(@"_mPercent:%f",_mPercent); // if(_mPercent > 1){ _mPercent = 1; }else if(_mPercent < 0){ _mPercent = 0; } _mPercent = 1 - _mPercent; _mProgress = _mPercent * (_mMaxValue - _mMinValue) + _mMinValue; if(_bTouchMove){ [self.mProgressChangedDelegate onProgressChanged:_mProgress]; } [self setNeedsDisplay]; } /** 根据进度值,计算当前 _mProgressAngle值 @param mProgress 进度值 */ -(void)setSeekBarProgressToValue:(int)mProgress{ if(mProgress < _mMinValue){ mProgress = _mMinValue; } if(mProgress > _mMaxValue){ mProgress = _mMaxValue; } _mProgress = mProgress; _mPercent = (_mProgress - _mMinValue) / (_mMaxValue - _mMinValue); // [self.mProgressChangedDelegate onProgressChanged:_mProgress]; } #pragma mark 设置进度条位置 /** 设置进度值 @param mProgress 进度值 */ -(void)setProgress:(int)mProgress{ [self setSeekBarProgressToValue:mProgress]; [self setNeedsDisplay]; } /** 设置进度显示值单位 @param mString 单位字符 */ -(void)setProgressBarUnitSring:(NSString *)mString{ _mProgressBarUnitSring = mString; } /** 设置最大值最小值 @param mMinValue 最小值 @param mMaxValue 最大值 */ -(void)setMinAndMaxValue:(float)mMinValue mMaxValue:(float)mMaxValue{ if(mMinValue < mMaxValue){ _mMinValue = mMinValue; _mMaxValue = mMaxValue; }else{ _mMinValue = mMaxValue; _mMaxValue = mMinValue; } _mProgress = _mMinValue; _mPercent = 0.0f; } /** 重置布局 @param mCGRect 布局 */ -(void)initWithFrameWaveSeekBar:(CGRect) mCGRect{ self.frame = mCGRect; [self refreshFrame]; } // ///** // 设置渐变效果 // // @param mColors 颜色数组 // */ //-(void)setProgressBarColors:(NSArray *)mColors{ // // // NSArray * colors = [NSArray arrayWithObjects:(id)startColor.CGColor,(id)endColor.CGColor,nil]; // //} /** 设置渐变效果 @param startColor 开始颜色 @param endColor 结束颜色 */ -(void)setProgressBarColors:(UIColor *)startColor endColor:(UIColor*)endColor{ // _mArcProgressBarColors = [NSArray arrayWithObjects(id)startColor.CGColor,(id)endColor.CGColor,nil]; _mWaveProgressBarColors = @[ (id)startColor.CGColor, (id)endColor.CGColor ]; } /** 设置进度条颜色 单一颜色 @param oneColor 单一颜色 */ -(void)setProgressBarColor:(UIColor *)oneColor{ // // _mArcProgressBarColors = [NSArray arrayWithObjects(id)startColor.CGColor,(id)endColor.CGColor,nil]; _mWaveProgressBarColors = @[ (id)oneColor.CGColor, (id)oneColor.CGColor ]; // _mWaveProgressBarColor = oneColor; } /** 设置边距 */ -(void)setWavePadding:(int)Padding{ _WavePadding = Padding; [self onWaveSizeChange]; } /** 设置背景边框宽度 */ -(void)setBorderWidth:(int)borderWidth{ _mBackLineWidth = borderWidth; [self onWaveSizeChange]; } /** 获取高度 Y值 */ -(CGFloat)getProgressY{ return _mCurtainY; } /** 刷新布局,更新关键值 */ -(void)onWaveSizeChange{ _mWavePadding = _WavePadding*2 + _mBackLineWidth; _mWaveHeight = self.frame.size.height - _mWavePadding*2; _mWaveWidth = self.frame.size.width - _mWavePadding*2; _mWaveStartX = _mWavePadding; _mWaveEndX = _mWaveWidth + _mWaveStartX; } @end