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-----------------------------