圆形进度条

begin-------------------------------.h----------------------------

#define CircleDegreeToRadian(d) ((d)*M_PI)/180.0

#define CircleRGB(r, g, b) [UIColor colorWithRed:(r)/255.0green:(g)/255.0blue:(b)/255.0alpha:1.0]

#define CircleSelfWidth self.frame.size.width

#define CircleSelfHeight self.frame.size.height

@interface CircleProgress : UIView

//图形定制

@property (nonatomic, strong) UIColor *pathBackColor;/**<线条背景色*/

@property (nonatomic, strong) UIColor *pathFillColor;/**<线条填充色*/

@property (nonatomic, strong) UIImageView *pointImage;/**<小圆点图片*/

@property (nonatomic, strong) UIImageView *coinImg;

@property (nonatomic, strong) CountingLabel *progressLabel;

//角度相关

@property (nonatomic, assign) CGFloat startAngle;/**<起点角度。角度从水平右侧开始为0,顺时针为增加角度。直接传度数 如-90 */

@property (nonatomic, assign) CGFloat reduceAngle;/**<减少的角度 直接传度数 如30*/

@property (nonatomic, assign) CGFloat strokeWidth;/**<线宽*/

@property (nonatomic, assign) CGFloat duration;/**<动画时长*/

@property (nonatomic, assign) BOOL showPoint;/**<是否显示小圆点*/

@property (nonatomic, assign) BOOL showProgressText;/**<是否显示文字*/

@property (nonatomic, assign) BOOL increaseFromLast;/**<是否从上次数值开始动画,默认为NO*/

@property (nonatomic, assign) BOOL prepareToShow;/**<是否准备好显示, 如果创建好就显示必须在设置完其他属性后最后设置此属性为YES*/

//进度

@property (nonatomic, assign) CGFloat progress;/**<进度 0-1 */

//初始化

- (instancetype)initWithFrame:(CGRect)frame

                pathBackColor:(UIColor*)pathBackColor

                pathFillColor:(UIColor*)pathFillColor

                   startAngle:(CGFloat)startAngle

                  strokeWidth:(CGFloat)strokeWidth;

end-------------------------------.h----------------------------

begin-------------------------------.m----------------------------

@interface CircleProgress ()<CAAnimationDelegate>

@property (nonatomic, strong) CAShapeLayer *backLayer;

@property (nonatomic, strong) CAShapeLayer *progressLayer;

@property (nonatomic, assign) CGFloat realWidth;//实际边长

@property (nonatomic, assign) CGFloat radius;//半径

@property (nonatomic, assign) CGFloat lastProgress;/**<上次进度 0-1 */

@property (nonatomic, strong) CAAnimation *lastPointAnimation;

@end

@implementation CircleProgress

- (instancetype)init {

    if(self= [superinit]) {

        [self initialization];

    }

    return self;

}

- (instancetype)initWithFrame:(CGRect)frame {

    if(self= [superinitWithFrame:frame]) {

        [self initialization];

    }

    return self;

}

- (void)awakeFromNib {

    [super awakeFromNib];

    [self initialization];

    //开始展示

    self.prepareToShow = YES;

}

//初始化

- (instancetype)initWithFrame:(CGRect)frame

                pathBackColor:(UIColor*)pathBackColor

                pathFillColor:(UIColor*)pathFillColor

                   startAngle:(CGFloat)startAngle

                  strokeWidth:(CGFloat)strokeWidth {

    if(self= [superinitWithFrame:frame]) {

        [self initialization];

        if(pathBackColor) {

            _pathBackColor= pathBackColor;

        }

        if(pathFillColor) {

            _pathFillColor= pathFillColor;

        }

        _startAngle=CircleDegreeToRadian(startAngle);

        _strokeWidth= strokeWidth;

    }

    return self;

}

//初始化数据

- (void)initialization {

    self.backgroundColor = [UIColor clearColor];

    _pathBackColor = [UIColor lightGrayColor];

    _pathFillColor = [UIColor redColor];

    _strokeWidth = 10;//线宽默认为10

    _startAngle = CircleDegreeToRadian(0);//圆起点位置

    _reduceAngle = CircleDegreeToRadian(0);//整个圆缺少的角度

    _duration = 1.5;//动画时长

    _showPoint = YES;//小圆点

    _showProgressText = YES;//文字

    _realWidth = CircleSelfWidth>CircleSelfHeight?CircleSelfHeight:CircleSelfWidth;

    _radius = _realWidth/2.0 - _strokeWidth/2.0;

}

#pragma Get

- (CAShapeLayer *)backLayer {

    if(!_backLayer) {

        _backLayer= [CAShapeLayerlayer];


        _backLayer.frame = CGRectMake((CircleSelfWidth-_realWidth)/2.0, (CircleSelfHeight-_realWidth)/2.0, _realWidth, _realWidth);


        _backLayer.fillColor =[UIColor colorWithRed:1/255.0 green:1/255.0 blue:1/255.0 alpha:0.3].CGColor;// [UIColor clearColor].CGColor;//填充色

        _backLayer.lineWidth = _strokeWidth;

        _backLayer.strokeColor = _pathBackColor.CGColor;

        _backLayer.lineCap=@"round";


        UIBezierPath*backCirclePath = [selfgetNewBezierPath];

        _backLayer.path= backCirclePath.CGPath;

    }

    return _backLayer;

}

- (CAShapeLayer *)progressLayer {

    if (!_progressLayer) {

        _progressLayer= [CAShapeLayerlayer];

        _progressLayer.frame = CGRectMake((CircleSelfWidth-_realWidth)/2.0, (CircleSelfHeight-_realWidth)/2.0, _realWidth, _realWidth);


        _progressLayer.fillColor = [UIColor clearColor].CGColor;//填充色

        _progressLayer.lineWidth = _strokeWidth;

        _progressLayer.strokeColor = _pathFillColor.CGColor;

        _progressLayer.lineCap = @"round";


        UIBezierPath*circlePath = [selfgetNewBezierPath];

        _progressLayer.path= circlePath.CGPath;

        _progressLayer.strokeEnd = 0.0;

    }

    return _progressLayer;

}

- (UIImageView *)pointImage {

    if (!_pointImage) {

        _pointImage= [[UIImageViewalloc]init];

      NSBundle*mainBundle = [NSBundlebundleForClass:[selfclass]];

        NSBundle*resourcesBundle = [NSBundlebundleWithPath:[mainBundlepathForResource:@"CircleProgress"ofType:@"bundle"]];

        _pointImage.image = [UIImage imageNamed:@"circle_point1" inBundle:resourcesBundle compatibleWithTraitCollection:nil];

        _pointImage.frame = CGRectMake(0, 0, _strokeWidth, _strokeWidth);

        //定位起点位置

        [self updatePointPosition];

    }

    return _pointImage;

}

- (CountingLabel *)progressLabel {

    if (!_progressLabel) {

        _progressLabel= [[CountingLabelalloc]init];

        _progressLabel.textColor = [UIColor blackColor];

        _progressLabel.textAlignment = NSTextAlignmentCenter;

        _progressLabel.font = [UIFont systemFontOfSize:22];

        _progressLabel.text = @"0%";

        _progressLabel.frame = CGRectMake(0, 0, CircleSelfWidth, CircleSelfHeight);

    }

    return _progressLabel;

}

-(UIImageView *)coinImg{

    if(!_coinImg) {

        _coinImg= [[UIImageViewalloc]init];

        _coinImg.frame = CGRectMake(CircleSelfWidth/4, CircleSelfHeight/4, CircleSelfWidth/2, CircleSelfHeight/2);

        _coinImg.image= [UIImageimageNamed:@"circle红包"];

        _coinImg.contentMode = UIViewContentModeScaleAspectFit;

    }

    return _coinImg;

}

#pragma Set

- (void)setStartAngle:(CGFloat)startAngle {

    if(_startAngle!=CircleDegreeToRadian(startAngle)) {

        _startAngle=CircleDegreeToRadian(startAngle);


        //如果已经创建了相关layer则重新创建

        if(_backLayer) {

            UIBezierPath*backCirclePath = [selfgetNewBezierPath];

            _backLayer.path= backCirclePath.CGPath;

        }


        if (_progressLayer) {

            UIBezierPath*circlePath = [selfgetNewBezierPath];

            _progressLayer.path= circlePath.CGPath;

            _progressLayer.strokeEnd=0.0;

        }


        if(_pointImage) {

            //更新圆点位置

            [self updatePointPosition];

        }

    }

}

- (void)setReduceAngle:(CGFloat)reduceAngle {

    if(_reduceAngle!=CircleDegreeToRadian(reduceAngle)) {

        if(reduceAngle>=360) {

            return;

        }

        _reduceAngle=CircleDegreeToRadian(reduceAngle);


        if(_backLayer) {

            UIBezierPath*backCirclePath = [selfgetNewBezierPath];

            _backLayer.path= backCirclePath.CGPath;

        }


        if (_progressLayer) {

            UIBezierPath*circlePath = [selfgetNewBezierPath];

            _progressLayer.path= circlePath.CGPath;

            _progressLayer.strokeEnd=0.0;

        }


        if(_pointImage) {

            //更新圆点位置

            [self updatePointPosition];

        }

    }

}

- (void)setStrokeWidth:(CGFloat)strokeWidth {

    if(_strokeWidth!= strokeWidth) {

        _strokeWidth= strokeWidth;


        _radius = _realWidth/2.0 - _strokeWidth/2.0;

        //设置线宽之后会导致radius改变,因此需要修改使用过strokeWidth和radius的属性

        if(_backLayer) {

            _backLayer.lineWidth = _strokeWidth;

            UIBezierPath*backCirclePath = [selfgetNewBezierPath];

            _backLayer.path= backCirclePath.CGPath;

        }

        if (_progressLayer) {

            _progressLayer.lineWidth = _strokeWidth;

            UIBezierPath*circlePath = [selfgetNewBezierPath];

            _progressLayer.path= circlePath.CGPath;

            _progressLayer.strokeEnd=0.0;

        }


        if(_pointImage) {

            _pointImage.frame = CGRectMake(0, 0, _strokeWidth, _strokeWidth);

            //更新圆点位置

            [self updatePointPosition];

        }

    }

}

- (void)setPathBackColor:(UIColor*)pathBackColor {

    if(_pathBackColor!= pathBackColor) {

        _pathBackColor= pathBackColor;

        if(_backLayer) {

            _backLayer.strokeColor = _pathBackColor.CGColor;

        }

    }

}

- (void)setPathFillColor:(UIColor*)pathFillColor {

    if(_pathFillColor!= pathFillColor) {

        _pathFillColor= pathFillColor;

        if (_progressLayer) {

            _progressLayer.strokeColor = _pathFillColor.CGColor;

        }

    }

}

- (void)setShowPoint:(BOOL)showPoint {

    if(_showPoint!= showPoint) {

        _showPoint= showPoint;

        if(_showPoint) {

            self.pointImage.hidden=NO;

            [self updatePointPosition];

        }else{

            self.pointImage.hidden=YES;

        }

    }

}

-(void)setShowProgressText:(BOOL)showProgressText {

    if(_showProgressText!= showProgressText) {

        _showProgressText= showProgressText;

        if (_showProgressText) {

            self.progressLabel.hidden=NO;

        }else{

            self.progressLabel.hidden=YES;

        }

    }

}

- (void)setPrepareToShow:(BOOL)prepareToShow {

    if(_prepareToShow!= prepareToShow) {

        _prepareToShow= prepareToShow;

        if (_prepareToShow) {

            [selfinitSubviews];

        }

    }

}

- (void)setProgress:(CGFloat)progress {

    //准备好显示

    self.prepareToShow = YES;

    _progress= progress;

    if(_progress<0) {

        _progress=0;

    }

    if(_progress>1) {

        _progress=1;

    }


    [self startAnimation];

}

- (void)startAnimation {

    //线条动画

    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];

    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

    pathAnimation.duration=_duration;

    pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

    pathAnimation.fromValue= [NSNumbernumberWithFloat:_increaseFromLast==YES?_lastProgress:0];

    pathAnimation.toValue= [NSNumbernumberWithFloat:_progress];

    pathAnimation.fillMode = kCAFillModeForwards;

    pathAnimation.removedOnCompletion=NO;

    [self.progressLayeraddAnimation:pathAnimationforKey:@"strokeEndAnimation"];


    if (_showPoint) {

        //小图片动画

        CAKeyframeAnimation *pointAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

        pointAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

        pointAnimation.fillMode=kCAFillModeForwards;

        pointAnimation.calculationMode=@"paced";

        pointAnimation.removedOnCompletion=NO;

        pointAnimation.duration=_duration;

        pointAnimation.delegate=self;


        BOOLclockwise =NO;

        if (_progress<_lastProgress && _increaseFromLast == YES) {

            clockwise =YES;

        }

        UIBezierPath *imagePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(_realWidth/2.0, _realWidth/2.0) radius:_radius startAngle:_increaseFromLast==YES?(2*M_PI-_reduceAngle)*_lastProgress+_startAngle:_startAngle endAngle:(2*M_PI-_reduceAngle)*_progress+_startAngle clockwise:!clockwise];

        pointAnimation.path= imagePath.CGPath;

        [self.pointImage.layeraddAnimation:pointAnimationforKey:nil];

        self.lastPointAnimation= pointAnimation;

        if (!_increaseFromLast && _progress == 0.0) {

            [self.pointImage.layer removeAllAnimations];

        }

    }

    if (_showProgressText) {

        if (_increaseFromLast) {

            [self.progressLabel countingFrom:_lastProgress*100 to:_progress*100 duration:_duration];

        }else{

            [self.progressLabel countingFrom:0 to:_progress*100 duration:_duration];

        }

    }

    _lastProgress = _progress;

}

//刷新最新路径

- (UIBezierPath *)getNewBezierPath {

    return [UIBezierPath bezierPathWithArcCenter:CGPointMake(_realWidth/2.0, _realWidth/2.0) radius:_radius startAngle:_startAngle endAngle:(2*M_PI-_reduceAngle+_startAngle) clockwise:YES];

}

//监听动画结束

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

    if(flag && anim ==_lastPointAnimation) {

        [self updatePointPosition];

    }

}

//更新小圆点的真实位置

- (void)updatePointPosition {

    //重定位起点

    CGFloat currentEndAngle = (2*M_PI-_reduceAngle)*_progress+_startAngle;

    [_pointImage.layer removeAllAnimations];

    _pointImage.center=CGPointMake(_realWidth/2.0+_radius*cosf(currentEndAngle),_realWidth/2.0+_radius*sinf(currentEndAngle));

}

- (void)initSubviews {

    [self.layer addSublayer:self.backLayer];

    [self.layer addSublayer:self.progressLayer];


    [selfaddSubview:self.pointImage];

    [self addSubview:self.progressLabel];

    [selfaddSubview:self.coinImg];

}

end-------------------------------.m----------------------------


begin------------------lable-----.h-----------------------------

@interface CountingLabel : UILabel

@property (nonatomic, assign) CGFloat duration;

- (void)countingFrom:(CGFloat)fromValueto:(CGFloat)toValue;

- (void)countingFrom:(CGFloat)fromValueto:(CGFloat)toValueduration:(CGFloat)duration;

end------------------lable-----.h-----------------------------

begin------------------lable-----.m-----------------------------

@interface CountingLabel ()

@property (nonatomic, strong) CADisplayLink *playLink;

@property (nonatomic, assign) NSInteger displayPerSecond;

@property (nonatomic, assign) CGFloat fromValue;

@property (nonatomic, assign) CGFloat toValue;

@property (nonatomic, assign) CGFloat increaseValue;

@property (nonatomic, assign) CGFloat perValue;

@end

@implementation CountingLabel

- (instancetype)init {

    if(self= [superinit]) {

        [selfinitValues];

    }

    return self;

}

- (instancetype)initWithFrame:(CGRect)frame {

    if(self= [superinitWithFrame:frame]) {

        [selfinitValues];

    }

    return self;

}

- (void)awakeFromNib {

    [super awakeFromNib];

    [self initValues];

}

- (void)initValues {

    _duration = 2.0;

    _displayPerSecond = 30;

}

- (void)countingFrom:(CGFloat)fromValueto:(CGFloat)toValue {

    [self countingFrom:fromValue to:toValue duration:_duration];

}

- (void)countingFrom:(CGFloat)fromValueto:(CGFloat)toValueduration:(CGFloat)duration {


    _fromValue= fromValue;

    _toValue= toValue;

    _duration= duration;


    _increaseValue = _fromValue;

    _perValue = (_toValue - _fromValue)/(_duration==0?1:(_displayPerSecond*_duration));


    if(self.playLink) {

        [self.playLinkinvalidate];

        self.playLink=nil;

    }


    CADisplayLink*playLink = [CADisplayLinkdisplayLinkWithTarget:selfselector:@selector(countingAction)];

    if(@available(iOS10.0, *)) {

        playLink.preferredFramesPerSecond = _displayPerSecond;

    }else{

        playLink.frameInterval = 60/(_displayPerSecond==0?1:_displayPerSecond);

    }


    [playLinkaddToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    [playLinkaddToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode];

    self.playLink= playLink;


}

- (void)countingAction {

    _increaseValue += _perValue;


    if (_fromValue < _toValue) {

        if (_increaseValue >= _toValue) {

            [selfstopDisplayLink];

        }

    }else{

        if (_increaseValue <= _toValue) {

            [selfstopDisplayLink];

        }

    }


    dispatch_async(dispatch_get_main_queue(), ^{

        self.text = [NSString stringWithFormat:@"%.0f%%",_increaseValue];

    });

}

- (void)stopDisplayLink {


    [self.playLink invalidate];

    self.playLink=nil;


    _increaseValue = _toValue;

}

@end

end------------------lable-----.m-----------------------------

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容