? ? ? 我們可以對繪制的Path進行分區。這兩個屬性的值在0~1之間,0代表Path的開始位置,1代表Path的結束位置。是一種線性遞增關系。strokeStart默認值為0,strokeEnd默認值為1。這兩個屬性都支持動畫。
比如說:strokeStart=0.1f; strokeEnd=0.7f則顯示如下圖所示? ?
實現效果如下圖所示:
具體實現代碼:
按照步驟上代碼:
/** *? 根據躍動數字 * *? 確定百分比 *? 現在的跳動數字——>背景顏色變化 * */
#import <UIKit/UIKit.h>
@interface DashboardView : UIView
@property (nonatomic, strong) UIImage *bgImage;
@property (nonatomic, copy) void(^TimerBlock)(NSInteger);
/**
*? 躍動數字刷新
*
*/
- (void)refreshJumpNOFromNO:(NSString *)startNO toNO:(NSString *)toNO;
@end
先列出一些基本變量和常量
#import "DashboardView.h"
#define degreesToRadians(x) (M_PI*(x)/180.0) //把角度轉換成PI的方式
static const CGFloat kMarkerRadius = 5.f; // 光標直徑
static const CGFloat kTimerInterval = 0.03; //定時器間隔
static const CGFloat kFastProportion = 0.9;?
static const NSInteger MaxNumber = 1000; //進度條最大值
@interface DashboardView () {
CGFloat animationTime; //動畫時間
NSInteger beginNO; //起始值
NSInteger jumpCurrentNO; //當前值
NSInteger endNO; //截止值
}
// 百分比 0 - 100 根據躍動數字設置
@property (nonatomic, assign) CGFloat percent; //百分比
@property (nonatomic, strong) CAShapeLayer *bottomLayer; // 進度條底色
@property (nonatomic, assign) CGFloat lineWidth; // 弧線寬度
@property (nonatomic, strong) UIImageView *markerImageView; // 光標
@property (nonatomic, strong) UIImageView *bgImageView; // 背景圖片
@property (nonatomic, assign) CGFloat circelRadius; //圓直徑
@property (nonatomic, assign) CGFloat startAngle; // 開始角度
@property (nonatomic, assign) CGFloat endAngle; // 結束角度
@property (nonatomic, strong) UILabel * showLable;//值顯示標簽
@property (nonatomic, strong) NSTimer * fastTimer;? //定時器
@property (nonatomic, strong) NSTimer * slowTimer; //定時器
@property (nonatomic, assign) NSInteger intervalNum; //間隔數
@end
#pragma mark - Life cycle
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.circelRadius = self.frame.size.width - 10.f;
self.lineWidth = 2.f;
self.startAngle = -200.f;
self.endAngle = 20.f;
// 尺寸需根據圖片進行調整
self.bgImageView.frame = CGRectMake(6, 6, self.circelRadius, self.circelRadius * 2 / 3);
self.bgImageView.backgroundColor = [UIColor clearColor];
[self addSubview:self.bgImageView];
//添加圓框
[self setupCircleBg];
//光標
[self setupMarkerImageView];
//添加躍動數字
[self setupJumpNOView];
}
return self;
}
- (void)setupCircleBg {
// 圓形路徑
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.width / 2, self.height / 2)
radius:(self.circelRadius - self.lineWidth) / 2
startAngle:degreesToRadians(self.startAngle)
endAngle:degreesToRadians(self.endAngle)
clockwise:YES];
// 底色
self.bottomLayer = [CAShapeLayer layer];
self.bottomLayer.frame = self.bounds;
self.bottomLayer.fillColor = [[UIColor clearColor] CGColor];
self.bottomLayer.strokeColor = [[UIColor? colorWithRed:206.f / 256.f green:241.f / 256.f blue:227.f alpha:1.f] CGColor];
self.bottomLayer.opacity = 0.5;
self.bottomLayer.lineCap = kCALineCapRound;
self.bottomLayer.lineWidth = self.lineWidth;
self.bottomLayer.path = [path CGPath];
[self.layer addSublayer:self.bottomLayer];
// 220 是用整個弧度的角度之和 |-200| + 20 = 220
//? ? [self createAnimationWithStartAngle:degreesToRadians(self.startAngle)
//? ? ? ? ? ? ? ? ? ? ? ? ? ? ? endAngle:degreesToRadians(self.startAngle + 220 * 1)];
}
- (void)setupMarkerImageView {
if (_markerImageView) {
return;
}
_markerImageView = [[UIImageView alloc] init];
_markerImageView.backgroundColor = [UIColor clearColor];
_markerImageView.layer.backgroundColor = [UIColor greenColor].CGColor;
_markerImageView.layer.shadowColor = [UIColor whiteColor].CGColor;
_markerImageView.layer.shadowOffset = CGSizeMake(0, 0);
_markerImageView.layer.shadowRadius = kMarkerRadius*0.5;
_markerImageView.layer.shadowOpacity = 1;
_markerImageView.layer.masksToBounds = NO;
self.markerImageView.layer.cornerRadius = self.markerImageView.frame.size.height / 2;
[self addSubview:self.markerImageView];
_markerImageView.frame = CGRectMake(-100, self.height, kMarkerRadius, kMarkerRadius);
}
- (void)setupJumpNOView {
if (_showLable) {
return;
}
CGFloat width = self.circelRadius / 2 + 20;
CGFloat height = self.circelRadius / 2;
CGFloat xPixel = self.bgImageView.left + (self.bgImageView.width - width)*0.5;//self.circelRadius / 4;
CGFloat yPixel = self.circelRadius / 4;
CGRect labelFrame = CGRectMake(xPixel, yPixel, width, height);
_showLable = [[UILabel alloc] initWithFrame:labelFrame];
_showLable.backgroundColor = [UIColor clearColor];
_showLable.textColor = [UIColor whiteColor];
_showLable.textAlignment = NSTextAlignmentCenter;
_showLable.font = [UIFont systemFontOfSize:70.f];
_showLable.text = [NSString stringWithFormat:@"%ld",jumpCurrentNO];
[self addSubview:_showLable];
}
#pragma mark - Animation
- (void)createAnimationWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle { // 光標動畫
//啟動定時器
[_fastTimer setFireDate:[NSDate distantPast]];
// 設置動畫屬性
CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
pathAnimation.calculationMode = kCAAnimationPaced;
pathAnimation.fillMode = kCAFillModeForwards;
pathAnimation.removedOnCompletion = NO;
pathAnimation.duration = _percent * kTimerInterval;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.repeatCount = 1;
// 設置動畫路徑
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, self.width / 2, self.height / 2, (self.circelRadius - kMarkerRadius / 2) / 2, startAngle, endAngle, 0);
pathAnimation.path = path;
CGPathRelease(path);
[self.markerImageView.layer addAnimation:pathAnimation forKey:@"moveMarker"];
}
#pragma mark - Setters / Getters
/**
*? 開始動畫? 確定百分比
*
*/
- (void)refreshJumpNOFromNO:(NSString *)startNO toNO:(NSString *)toNO {
beginNO = 0;//[startNO integerValue];
jumpCurrentNO = 0;//[startNO integerValue];
endNO = [toNO integerValue];
_percent = endNO * 100 / MaxNumber;
NSInteger diffNum = endNO - beginNO;
if (diffNum <= 0) {
return;
}
if (diffNum < 100) {
_intervalNum = 5;
} else if (diffNum < 300) {
_intervalNum = 15;
} else if (diffNum <= MaxNumber) {
_intervalNum = 10;
}
NSLog(@"數字間隔:%ld",_intervalNum);
//數字
[self setupJumpThings];
//光標
[self createAnimationWithStartAngle:degreesToRadians(self.startAngle)
endAngle:degreesToRadians(self.startAngle + 220 * _percent / 100)];
}
- (void)setBgImage:(UIImage *)bgImage {
_bgImage = bgImage;
self.bgImageView.image = bgImage;
}
- (UIImageView *)bgImageView {
if (nil == _bgImageView) {
_bgImageView = [[UIImageView alloc] init];
}
return _bgImageView;
}
#pragma mark - 躍動數字
- (void)setupJumpThings {
animationTime = _percent * kTimerInterval;
self.fastTimer = [NSTimer timerWithTimeInterval:kTimerInterval*kFastProportion
target:self
selector:@selector(fastTimerAction)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_fastTimer forMode:NSRunLoopCommonModes];
//時間間隔 = (總時間 - 快時間間隔*變化次數)/ 再次需要變化的次數
//快時間
NSInteger fastEndNO = endNO * kFastProportion;
NSInteger fastJump = fastEndNO/_intervalNum;
if (fastJump % _intervalNum) {
fastJump++;
fastEndNO += _intervalNum;
}
CGFloat fastTTime = fastJump*kTimerInterval*kFastProportion;
//剩余應跳動次數
NSInteger changNO = endNO - fastEndNO;
NSInteger endJump = changNO / _intervalNum + changNO % _intervalNum;
//慢時間間隔
NSTimeInterval slowInterval = (animationTime - fastTTime) / endJump;
self.slowTimer = [NSTimer timerWithTimeInterval:slowInterval
target:self
selector:@selector(slowTimerAction)
userInfo:nil
repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_slowTimer forMode:NSRunLoopCommonModes];
[_fastTimer setFireDate:[NSDate distantFuture]];
[_slowTimer setFireDate:[NSDate distantFuture]];
}
#pragma mark 加速定時器觸發事件
- (void)fastTimerAction {
if (jumpCurrentNO >= endNO) {
[self.fastTimer invalidate];
return;
}
if (jumpCurrentNO >= endNO * kFastProportion) {
[self.fastTimer invalidate];
[self.slowTimer setFireDate:[NSDate distantPast]];
return;
}
[self commonTimerAction];
}
#pragma mark 減速定時器觸發事件
- (void)slowTimerAction {
if (jumpCurrentNO >= endNO) {
[self.slowTimer invalidate];
return;
}
[self commonTimerAction];
}
#pragma mark 計時器共性事件 - lable賦值 背景顏色變化
- (void)commonTimerAction {
if (jumpCurrentNO % 100 == 0 && jumpCurrentNO != 0) {
NSInteger colorIndex = jumpCurrentNO / 100;
dispatch_async(dispatch_get_main_queue(), ^{
if (self.TimerBlock) {
self.TimerBlock(colorIndex);
}
});
}
NSInteger changeValueBy = endNO - jumpCurrentNO;
if (changeValueBy/10 < 1) {
jumpCurrentNO++;
} else {
//? ? ? ? NSInteger changeBy = changeValueBy / 10;
jumpCurrentNO += _intervalNum;
}
_showLable.text = [NSString stringWithFormat:@"%ld",jumpCurrentNO];
}
@end