前言
最近項(xiàng)目和股票有關(guān),自然而然想起了分時線,K線等東西.即使是出自自己手里的東西,放久了難覓看忘記,特別是當(dāng)時的想法和邏輯思維過程.既然寫了,總要留下點(diǎn)什么,于是有了這篇筆記.
先看看最后結(jié)果吧.此圖確實(shí)不夠美觀,但是畢竟只是一個demo,一個骨架而已,具體的血肉需要大家自己根據(jù)需要去填充.

1.png
畫分時線,我們需要做些什么?
- 畫框架(網(wǎng)格線);
- 橫坐標(biāo)上的文字(時間軸);
- 縱坐標(biāo)上的文字(左邊價格,右邊漲跌率);
- 畫分時線;
- 心跳效果;
- 添加手勢(長按和點(diǎn)擊);
- 畫十字交叉線;
- 畫十字交叉線與橫縱坐標(biāo)交點(diǎn)處的文字;
下面一步步來實(shí)現(xiàn)上面的效果吧
0.工欲善其事必先利其器,先來抽幾個方法吧
- 心跳效果
/**心跳動畫*/
- (void)heartAnimationWithLayer:(CALayer*)layer {
CAAnimationGroup *group = [[CAAnimationGroup alloc]init];
group.duration = 0.5;
group.repeatCount = HUGE;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.toValue = @1.2;
CABasicAnimation *alphaAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnim.toValue = @0.3f;
group.animations = @[scaleAnim,alphaAnim];
[layer addAnimation:group forKey:nil];
}
- 坐標(biāo)軸的轉(zhuǎn)換
/**根據(jù)時間獲取坐標(biāo)點(diǎn)*/
-(CGFloat)miniteTimeWithTimeStr:(NSString*)timeStr {
if (timeStr == nil || [timeStr isEqualToString:@""]) return 0.0;
NSArray *temp = [timeStr componentsSeparatedByString:@":"];
NSInteger minte = [temp[0] integerValue]*60 + [temp[1] integerValue];
//每分鐘代表的寬度
CGFloat aveWidth = DPW/ 240.0;
if (minte <= 11*60+30) {//上午
return aveWidth*(minte - 9*60-30);
}else {//下午
return DPW *0.5 + aveWidth*(minte - 13*60);
}
}
/**根據(jù)坐標(biāo)點(diǎn)獲取對應(yīng)時間*/
-(NSString*)timeWithPoint:(CGPoint)point{
NSString *str = @"";
//單位距離代表的時間
CGFloat aveWidth = 240.0/DPW;
CGFloat totalTime = aveWidth * point.x + 9*60 + 30;//上午
if (point.x >DPW *0.5) {
totalTime = aveWidth * point.x + 11*60;//下午
}
int h = totalTime / 60;
int m = ((int)(totalTime + 0.5)) % 60;
str = [NSString stringWithFormat:@"%d:%d",h,m];
return str;
}
/**根據(jù)價格獲取坐標(biāo)位置*/
- (CGFloat)priceLabelLocationWithPrice:(CGFloat)price {
CGFloat height = DPH - 60;
CGFloat avrSpace = height/(self.max - self.min);
return height - ((price - self.min)*avrSpace);
}
/**根據(jù)坐標(biāo)位置獲取價格*/
- (NSString*)pricWithPoint:(CGPoint)point {
NSString *price = @"";
//單位距離代表的價格
CGFloat avePrice = (self.max - self.min)/(DPH - 60);
//保留兩位小數(shù)
price = [NSString stringWithFormat:@"%.2f",(self.max - point.y*avePrice)+0.005];
return price;
}
- 畫文字
/**畫文字,直接創(chuàng)建一個CATextLayer*/
- (void)drawLabelAtRect:(CGRect)rect textStr:(NSString*)textStr {
CATextLayer *textLayer = [CATextLayer layer];
textLayer.frame = rect;
[self.layer addSublayer:textLayer];
//set text attributes
textLayer.foregroundColor = [UIColor blackColor].CGColor;
textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;
//choose a font
UIFont *font = [UIFont systemFontOfSize:10];
//set layer font
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
CGFontRelease(fontRef);
textLayer.contentsScale = [UIScreen mainScreen].scale;
//choose some text
//set layer text
textLayer.string = textStr;
}
/**畫文字,指定CATextLayer*/
- (void)drawCrossLabelWithTextLayer:(CATextLayer*)textLayer AtRect:(CGRect)rect textStr:(NSString*)textStr {
textLayer.frame = rect;
[self.layer addSublayer:textLayer];
//set text attributes
textLayer.foregroundColor = [UIColor blackColor].CGColor;
textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;
//choose a font
UIFont *font = [UIFont systemFontOfSize:10];
//set layer font
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
CGFontRelease(fontRef);
textLayer.contentsScale = [UIScreen mainScreen].scale;
//choose some text
//set layer text
textLayer.string = textStr;
}
1.獲取縱坐標(biāo)的最大值,最小值和中間值
說明:
- 橫線:分時線最中間的橫線對應(yīng)昨日收盤價,這是固定的.但是,其他橫線是根據(jù)數(shù)據(jù)實(shí)時變化的,旨在包含所有的數(shù)據(jù)點(diǎn).所以,max = mid + 最大偏差;min = mid - 最大偏差;最大偏差=所有數(shù)據(jù)中偏離mid最大的價格與mid之間差值的絕對值.這樣max - min 為價格的波動范圍,有了這個就可以根據(jù)需要,畫出橫線(這里畫了3條橫線);
- 豎線: 股票交易時間,上午9:30到11:30,下午13:00到15:00,總共4個小時;這里就可以算出單位距離代表的時間,從而確定每個時間節(jié)點(diǎn)在橫坐標(biāo)上的位置,畫出橫坐標(biāo).同理,也可算出每分鐘代表的長度,這點(diǎn)在畫十字交叉線與X軸的交點(diǎn)文字時用的到.對應(yīng)的方法見上面已抽取的.
- 分時線: 可以看到在這個方法中已近開始劃線了.劃線無非就是連線,moveToPoint 和addLineToPoint.為了避免再次遍歷所有數(shù)據(jù),所以在確定最大和最小價格的循環(huán)中就開始添加線;
/**數(shù)據(jù)處理*/
- (void)preworkForData {
//0 拿到縱坐標(biāo)的3個點(diǎn)(max,mid,min),mid=昨日收盤價
DPTimeLineItem *temp = self.dataArrM[0];
self.mid = [temp.pre_close_px doubleValue];
if ([temp.last_px doubleValue] -[temp.pre_close_px doubleValue] > 0) {
self.max = [temp.last_px doubleValue];
self.min = [temp.pre_close_px doubleValue] -([temp.last_px doubleValue] -[temp.pre_close_px doubleValue] );
}else {
self.min = [temp.last_px doubleValue];
self.max = [temp.pre_close_px doubleValue] + ([temp.pre_close_px doubleValue] -[temp.last_px doubleValue] );
}
for (int i = 0; i< self.dataArrM.count; i ++ ) {
DPTimeLineItem *item = self.dataArrM[i];
//獲取縱坐標(biāo)最大,最小,中間值
if (fabs(item.last_px.doubleValue - self.mid) >(self.max - self.mid)) {
self.max = self.mid +fabs(item.last_px.doubleValue - self.mid);
self.min = self.mid - fabs(item.last_px.doubleValue - self.mid);
}
//畫分時線
CGFloat timeX = [self miniteTimeWithTimeStr:item.curr_time];
CGFloat priceY = [self priceLabelLocationWithPrice:item.last_px.floatValue];
if (i == 0) {
if (self.isNeedBackGroundColor) {
[self.timeLinePath moveToPoint:CGPointMake(0, DPH - 60)];
[self.timeLinePath addLineToPoint:CGPointMake(timeX, priceY)];
}else {
[self.timeLinePath moveToPoint:CGPointMake(timeX, priceY)];
}
}
if (i == self.dataArrM.count-1) {
self.currentPoint = CGPointMake(timeX, priceY);
}
[self.timeLinePath addLineToPoint:CGPointMake(timeX, priceY)];
}
}
2.畫網(wǎng)格線
說明:
- 利用for循環(huán),畫橫豎線;
- 橫縱坐標(biāo)固定地方畫文字
/**畫框架*/
- (void)drawFramework {
UIBezierPath *path = [[UIBezierPath alloc]init];
//三條橫線
CGFloat rowSpace = (DPH -60.0)/2.0;
NSString *tempStr = @"";
for (int i = 0; i < 3; i ++) {
[path moveToPoint:CGPointMake(0,rowSpace*i)];
[path addLineToPoint:CGPointMake(DPW, rowSpace*i)];
if (0 == i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.max];
}else if (1==i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.mid];
}else if (2 == i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.min];
}
[self drawLabelAtRect:CGRectMake(-25, rowSpace*i-10, 25, 20) textStr:tempStr];
}
//4條豎線
CGFloat colSpace = DPW / 4.0;
for (int i = 0; i < 5; i ++) {
[path moveToPoint:CGPointMake(colSpace*i,0)];
[path addLineToPoint:CGPointMake(colSpace*i, DPH-60)];
if (0 == i) {
tempStr = @"9:30";
[self drawLabelAtRect:CGRectMake(colSpace*i, DPH-60 ,30,20) textStr:tempStr];
continue;
}else if (1==i) {
tempStr = @"10:30";
}else if (2 == i) {
tempStr = @"11:30/13:00";
[self drawLabelAtRect:CGRectMake(colSpace*i-30, DPH-60 ,70,20) textStr:tempStr];
continue;
}else if (3==i) {
tempStr = @"14:00";
}else if (4 == i) {
tempStr = @"15:00";
[self drawLabelAtRect:CGRectMake(colSpace*i - 30, DPH-60 ,30,20) textStr:tempStr];
continue;
}
[self drawLabelAtRect:CGRectMake(colSpace*i-15, DPH-60 ,35,20) textStr:tempStr];
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc]init];
shapeLayer.strokeColor = [UIColor lightGrayColor].CGColor;
shapeLayer.path = path.CGPath;
[self.layer addSublayer:shapeLayer];
}
3.畫坐標(biāo)軸上的文字
見2.畫網(wǎng)格線
4.畫分時線
說明:這里通過isNeedBackGroundColor控制是否畫背景
- (void)drawTimeLine {
if (self.isNeedBackGroundColor) {
[self.timeLinePath addLineToPoint:CGPointMake(self.currentPoint.x , DPH - 60)];
self.timeLineLayer.fillColor = [UIColor lightGrayColor].CGColor;
}else {
self.timeLineLayer.fillColor = [UIColor clearColor].CGColor;
}
self.timeLineLayer.path = self.timeLinePath.CGPath;
[self.layer addSublayer:self.timeLineLayer];
}
5.心跳效果
/**添加心跳效果*/
- (void)setupheartLayer {
self.heartLayer.frame = CGRectMake(0, 0, 4, 4);
self.heartLayer.position = self.currentPoint;
self.heartLayer.cornerRadius = _heartLayer.frame.size.width*0.5;
self.heartLayer.masksToBounds = YES;
[self.layer addSublayer:self.heartLayer];
[self heartAnimationWithLayer:self.heartLayer];
}
6.添加手勢
說明:
- 長按手勢 :觸發(fā)方法畫十字交叉線及其與坐標(biāo)軸的交叉點(diǎn)文字
- 點(diǎn)擊手勢: 影藏十字交叉線及對應(yīng)文字
/**添加手勢*/
- (void)setupGestureRecognize {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
[self addGestureRecognizer:longPress];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
[self addGestureRecognizer:tap];
}
/**長按*/
- (void)longPressAction:(UILongPressGestureRecognizer*)gesture {
CGPoint tempPoint = [gesture locationInView:self];
//越界控制
if (tempPoint.x >= DPW) {
tempPoint = CGPointMake(DPW, tempPoint.y);
}
if (tempPoint.x <= 0.0) {
tempPoint = CGPointMake(0, tempPoint.y);
}
if (tempPoint.y >= DPH - 60) {
tempPoint = CGPointMake(tempPoint.x, DPH-60);
}
if (tempPoint.y <= 0.0) {
tempPoint = CGPointMake(tempPoint.x, 0);
}
if (gesture.state == UIGestureRecognizerStateBegan) {
[self drawCrossLineWithPoint:tempPoint];
}else if (gesture.state == UIGestureRecognizerStateChanged) {
[self drawCrossLineWithPoint:tempPoint];
}else if (gesture.state == UIGestureRecognizerStateEnded) {
}
}
/**點(diǎn)擊*/
- (void)tap:(UITapGestureRecognizer*) gesture {
self.crossLayer.path = nil;
[self.crossPriceLayer removeFromSuperlayer];
[self.crossTimeLayer removeFromSuperlayer];
}
完整代碼 拿走不謝
#import "DPTimeLineView.h"
#import "DPTimeLineItem.h"
#define DPW self.frame.size.width
#define DPH self.frame.size.height
@interface DPTimeLineView ()
/**縱坐標(biāo)最大值*/
@property (nonatomic, assign) double max;
/**縱坐標(biāo)中間值*/
@property (nonatomic, assign) double mid;
/**縱坐標(biāo)最小值*/
@property (nonatomic, assign) double min;
/**分時線路徑*/
@property (nonatomic, strong) UIBezierPath *timeLinePath;
/**最新點(diǎn)坐標(biāo)*/
@property (nonatomic, assign) CGPoint currentPoint;
/**是否顯示背景*/
@property (nonatomic, assign) BOOL isNeedBackGroundColor;
/**心跳點(diǎn)*/
@property (nonatomic, strong) CALayer *heartLayer;
/**分時線*/
@property (nonatomic, strong) CAShapeLayer *timeLineLayer;
/**十字光標(biāo)*/
@property (nonatomic, strong) CAShapeLayer *crossLayer;
/**十字光標(biāo)時間軸文字*/
@property (nonatomic, strong) CATextLayer *crossTimeLayer;
/**十字光標(biāo)價格軸文字*/
@property (nonatomic, strong) CATextLayer *crossPriceLayer;
@end
@implementation DPTimeLineView
#pragma mark - 入口
//alloc方法調(diào)用
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
[self setupGestureRecognize];
}
return self;
}
//load nib 調(diào)用
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
self.backgroundColor = [UIColor whiteColor];
[self setupGestureRecognize];
}
return self;
}
#pragma mark - 懶加載
- (UIBezierPath *)timeLinePath {
if (!_timeLinePath) {
_timeLinePath = [[UIBezierPath alloc]init];
}
return _timeLinePath;
}
- (CALayer *)heartLayer {
if (!_heartLayer) {
_heartLayer = [CALayer layer];
_heartLayer.backgroundColor = [UIColor blueColor].CGColor;
}
return _heartLayer;
}
- (CAShapeLayer *)timeLineLayer {
if (!_timeLineLayer) {
_timeLineLayer = [[CAShapeLayer alloc]init];
_timeLineLayer.strokeColor = [UIColor redColor].CGColor;
_timeLineLayer.lineWidth = 0.5f;
}
return _timeLineLayer;
}
- (CAShapeLayer *)crossLayer {
if (!_crossLayer) {
_crossLayer =[[CAShapeLayer alloc]init];
_crossLayer.lineDashPattern = @[@1, @2];//畫虛線
}
return _crossLayer;
}
- (CATextLayer *)crossPriceLayer {
if (!_crossPriceLayer) {
_crossPriceLayer = [[CATextLayer alloc]init];
}
return _crossPriceLayer;
}
- (CATextLayer *)crossTimeLayer {
if (!_crossTimeLayer) {
_crossTimeLayer = [[CATextLayer alloc]init];
}
return _crossTimeLayer;
}
#pragma mark - GestureRecognize
/**添加手勢*/
- (void)setupGestureRecognize {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPressAction:)];
[self addGestureRecognizer:longPress];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
[self addGestureRecognizer:tap];
}
/**長按*/
- (void)longPressAction:(UILongPressGestureRecognizer*)gesture {
CGPoint tempPoint = [gesture locationInView:self];
//越界控制
if (tempPoint.x >= DPW) {
tempPoint = CGPointMake(DPW, tempPoint.y);
}
if (tempPoint.x <= 0.0) {
tempPoint = CGPointMake(0, tempPoint.y);
}
if (tempPoint.y >= DPH - 60) {
tempPoint = CGPointMake(tempPoint.x, DPH-60);
}
if (tempPoint.y <= 0.0) {
tempPoint = CGPointMake(tempPoint.x, 0);
}
if (gesture.state == UIGestureRecognizerStateBegan) {
[self drawCrossLineWithPoint:tempPoint];
}else if (gesture.state == UIGestureRecognizerStateChanged) {
[self drawCrossLineWithPoint:tempPoint];
}else if (gesture.state == UIGestureRecognizerStateEnded) {
}
}
/**點(diǎn)擊*/
- (void)tap:(UITapGestureRecognizer*) gesture {
self.crossLayer.path = nil;
[self.crossPriceLayer removeFromSuperlayer];
[self.crossTimeLayer removeFromSuperlayer];
}
#pragma mark - 數(shù)據(jù)入口
- (void)setDataArrM:(NSMutableArray<DPTimeLineItem *> *)dataArrM {
_dataArrM = dataArrM;
//0.數(shù)據(jù)處理,拿到縱坐標(biāo)數(shù)據(jù)
[self preworkForData];
//1.畫框架
[self drawFramework];
//2.畫文字
//3.畫分時線
[self drawTimeLine];
//4.畫末尾的心跳點(diǎn)
[self setupheartLayer];
}
#pragma mark - 畫圖方法
- (void)drawTimeLine {
if (self.isNeedBackGroundColor) {
[self.timeLinePath addLineToPoint:CGPointMake(self.currentPoint.x , DPH - 60)];
self.timeLineLayer.fillColor = [UIColor lightGrayColor].CGColor;
}else {
self.timeLineLayer.fillColor = [UIColor clearColor].CGColor;
}
self.timeLineLayer.path = self.timeLinePath.CGPath;
[self.layer addSublayer:self.timeLineLayer];
}
/**畫框架*/
- (void)drawFramework {
UIBezierPath *path = [[UIBezierPath alloc]init];
//三條橫線
CGFloat rowSpace = (DPH -60.0)/2.0;
NSString *tempStr = @"";
for (int i = 0; i < 3; i ++) {
[path moveToPoint:CGPointMake(0,rowSpace*i)];
[path addLineToPoint:CGPointMake(DPW, rowSpace*i)];
if (0 == i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.max];
}else if (1==i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.mid];
}else if (2 == i) {
tempStr = [NSString stringWithFormat:@"%.2f",self.min];
}
[self drawLabelAtRect:CGRectMake(-25, rowSpace*i-10, 25, 20) textStr:tempStr];
}
//4條豎線
CGFloat colSpace = DPW / 4.0;
for (int i = 0; i < 5; i ++) {
[path moveToPoint:CGPointMake(colSpace*i,0)];
[path addLineToPoint:CGPointMake(colSpace*i, DPH-60)];
if (0 == i) {
tempStr = @"9:30";
[self drawLabelAtRect:CGRectMake(colSpace*i, DPH-60 ,30,20) textStr:tempStr];
continue;
}else if (1==i) {
tempStr = @"10:30";
}else if (2 == i) {
tempStr = @"11:30/13:00";
[self drawLabelAtRect:CGRectMake(colSpace*i-30, DPH-60 ,70,20) textStr:tempStr];
continue;
}else if (3==i) {
tempStr = @"14:00";
}else if (4 == i) {
tempStr = @"15:00";
[self drawLabelAtRect:CGRectMake(colSpace*i - 30, DPH-60 ,30,20) textStr:tempStr];
continue;
}
[self drawLabelAtRect:CGRectMake(colSpace*i-15, DPH-60 ,35,20) textStr:tempStr];
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc]init];
shapeLayer.strokeColor = [UIColor lightGrayColor].CGColor;
shapeLayer.path = path.CGPath;
[self.layer addSublayer:shapeLayer];
}
/**畫文字,直接創(chuàng)建一個CATextLayer*/
- (void)drawLabelAtRect:(CGRect)rect textStr:(NSString*)textStr {
CATextLayer *textLayer = [CATextLayer layer];
textLayer.frame = rect;
[self.layer addSublayer:textLayer];
//set text attributes
textLayer.foregroundColor = [UIColor blackColor].CGColor;
textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;
//choose a font
UIFont *font = [UIFont systemFontOfSize:10];
//set layer font
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
CGFontRelease(fontRef);
textLayer.contentsScale = [UIScreen mainScreen].scale;
//choose some text
//set layer text
textLayer.string = textStr;
}
/**畫文字,指定CATextLayer*/
- (void)drawCrossLabelWithTextLayer:(CATextLayer*)textLayer AtRect:(CGRect)rect textStr:(NSString*)textStr {
textLayer.frame = rect;
[self.layer addSublayer:textLayer];
//set text attributes
textLayer.foregroundColor = [UIColor blackColor].CGColor;
textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;
//choose a font
UIFont *font = [UIFont systemFontOfSize:10];
//set layer font
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
CGFontRelease(fontRef);
textLayer.contentsScale = [UIScreen mainScreen].scale;
//choose some text
//set layer text
textLayer.string = textStr;
}
/**畫十字光標(biāo)線*/
- (void)drawCrossLineWithPoint:(CGPoint)point {
UIBezierPath * path = [[UIBezierPath alloc]init];
[path moveToPoint:CGPointMake(point.x, 0)];
[path addLineToPoint:CGPointMake(point.x, DPH - 60)];
[path moveToPoint:CGPointMake(0, point.y)];
[path addLineToPoint:CGPointMake(DPH, point.y)];
self.crossLayer.strokeColor = [UIColor blueColor].CGColor;
self.crossLayer.path = path.CGPath;
[self.layer addSublayer: self.crossLayer];
//畫坐標(biāo)點(diǎn)對于文字
NSString *price = [self pricWithPoint:point];
NSString *time = [self timeWithPoint:point];
[self drawCrossLabelWithTextLayer:self.crossTimeLayer AtRect:CGRectMake(point.x, DPH-60, 30, 20) textStr:time];
[self drawCrossLabelWithTextLayer:self.crossPriceLayer AtRect:CGRectMake(0, point.y, 30, 20) textStr:price];
}
/**添加心跳效果*/
- (void)setupheartLayer {
self.heartLayer.frame = CGRectMake(0, 0, 4, 4);
self.heartLayer.position = self.currentPoint;
self.heartLayer.cornerRadius = _heartLayer.frame.size.width*0.5;
self.heartLayer.masksToBounds = YES;
[self.layer addSublayer:self.heartLayer];
[self heartAnimationWithLayer:self.heartLayer];
}
#pragma mark - Others
/**數(shù)據(jù)處理*/
- (void)preworkForData {
//0 拿到縱坐標(biāo)的3個點(diǎn)(max,mid,min),mid=昨日收盤價
DPTimeLineItem *temp = self.dataArrM[0];
self.mid = [temp.pre_close_px doubleValue];
if ([temp.last_px doubleValue] -[temp.pre_close_px doubleValue] > 0) {
self.max = [temp.last_px doubleValue];
self.min = [temp.pre_close_px doubleValue] -([temp.last_px doubleValue] -[temp.pre_close_px doubleValue] );
}else {
self.min = [temp.last_px doubleValue];
self.max = [temp.pre_close_px doubleValue] + ([temp.pre_close_px doubleValue] -[temp.last_px doubleValue] );
}
for (int i = 0; i< self.dataArrM.count; i ++ ) {
DPTimeLineItem *item = self.dataArrM[i];
//獲取縱坐標(biāo)最大,最小,中間值
if (fabs(item.last_px.doubleValue - self.mid) >(self.max - self.mid)) {
self.max = self.mid +fabs(item.last_px.doubleValue - self.mid);
self.min = self.mid - fabs(item.last_px.doubleValue - self.mid);
}
//畫分時線
CGFloat timeX = [self miniteTimeWithTimeStr:item.curr_time];
CGFloat priceY = [self priceLabelLocationWithPrice:item.last_px.floatValue];
if (i == 0) {
if (self.isNeedBackGroundColor) {
[self.timeLinePath moveToPoint:CGPointMake(0, DPH - 60)];
[self.timeLinePath addLineToPoint:CGPointMake(timeX, priceY)];
}else {
[self.timeLinePath moveToPoint:CGPointMake(timeX, priceY)];
}
}
if (i == self.dataArrM.count-1) {
self.currentPoint = CGPointMake(timeX, priceY);
}
[self.timeLinePath addLineToPoint:CGPointMake(timeX, priceY)];
}
}
/**根據(jù)時間獲取坐標(biāo)點(diǎn)*/
-(CGFloat)miniteTimeWithTimeStr:(NSString*)timeStr {
if (timeStr == nil || [timeStr isEqualToString:@""]) return 0.0;
NSArray *temp = [timeStr componentsSeparatedByString:@":"];
NSInteger minte = [temp[0] integerValue]*60 + [temp[1] integerValue];
//每分鐘代表的寬度
CGFloat aveWidth = DPW/ 240.0;
if (minte <= 11*60+30) {//上午
return aveWidth*(minte - 9*60-30);
}else {//下午
return DPW *0.5 + aveWidth*(minte - 13*60);
}
}
/**根據(jù)坐標(biāo)點(diǎn)獲取對應(yīng)時間*/
-(NSString*)timeWithPoint:(CGPoint)point{
NSString *str = @"";
//單位距離代表的時間
CGFloat aveWidth = 240.0/DPW;
CGFloat totalTime = aveWidth * point.x + 9*60 + 30;//上午
if (point.x >DPW *0.5) {
totalTime = aveWidth * point.x + 11*60;//下午
}
int h = totalTime / 60;
int m = ((int)(totalTime + 0.5)) % 60;
str = [NSString stringWithFormat:@"%d:%d",h,m];
return str;
}
/**根據(jù)價格獲取坐標(biāo)位置*/
- (CGFloat)priceLabelLocationWithPrice:(CGFloat)price {
CGFloat height = DPH - 60;
CGFloat avrSpace = height/(self.max - self.min);
return height - ((price - self.min)*avrSpace);
}
/**根據(jù)坐標(biāo)位置獲取價格*/
- (NSString*)pricWithPoint:(CGPoint)point {
NSString *price = @"";
//單位距離代表的價格
CGFloat avePrice = (self.max - self.min)/(DPH - 60);
//保留兩位小數(shù)
price = [NSString stringWithFormat:@"%.2f",(self.max - point.y*avePrice)+0.005];
return price;
}
/**心跳動畫*/
- (void)heartAnimationWithLayer:(CALayer*)layer {
CAAnimationGroup *group = [[CAAnimationGroup alloc]init];
group.duration = 0.5;
group.repeatCount = HUGE;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleAnim.toValue = @1.2;
CABasicAnimation *alphaAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
alphaAnim.toValue = @0.3f;
group.animations = @[scaleAnim,alphaAnim];
[layer addAnimation:group forKey:nil];
}
提供一個數(shù)據(jù)入口,每次收到數(shù)據(jù)就重新劃線
@class DPTimeLineItem;
@interface DPTimeLineView : UIView
/**分時數(shù)據(jù)列表*/
@property (nonatomic, strong)NSMutableArray<DPTimeLineItem*> *dataArrM;
@end
#pragma mark - 數(shù)據(jù)入口
- (void)setDataArrM:(NSMutableArray<DPTimeLineItem *> *)dataArrM {
_dataArrM = dataArrM;
//0.數(shù)據(jù)處理,拿到縱坐標(biāo)數(shù)據(jù)
[self preworkForData];
//1.畫框架
[self drawFramework];
//2.畫文字
//3.畫分時線
[self drawTimeLine];
//4.畫末尾的心跳點(diǎn)
[self setupheartLayer];
}
備注:本文中之所以主要使用CAShapeLayer來畫線,主要在于它的以下優(yōu)勢;
- 渲染快,采用硬件加速,比用Core Graphics快很多;
- 高效使用內(nèi)存,由于不需要像普通layer那樣創(chuàng)建寄宿圖,所以無論改layer多大,都不要占用太多內(nèi)存;
- 超出圖層依然可以繪圖,不會被圖層邊界裁剪掉;
- 不會出現(xiàn)像素化, CAShapeLayer在做3D變換時,不會像普通有寄宿圖的CALayer那樣變得像素畫.