iOS-手把手教你畫分時線

前言

最近項(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那樣變得像素畫.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 初春的夜 死一般的靜寂 靜寂的連大地都停止了呼吸 那些曾經(jīng)的盛宴花開 魚游蝦悅 蟬鳴鳥叫 地膚潔白 在此刻竟沒...
    小昭11閱讀 291評論 0 0
  • 很多年以前在報(bào)紙上看的暢銷書排行榜上看到過《消失的地平線》。最近在圖書館無意中看到了它,于是借回家閱讀。 小說很短...
    梓漆閱讀 1,112評論 0 2
  • 最近情緒一直往下掉,就像被人丟進(jìn)夜晚的湖里,掙扎著,掙扎著,終于露出水面,仿佛已經(jīng)得救了,卻又忽的一下子又被不知哪...
    四代法尼亞閱讀 213評論 4 2
  • 嘿,你看到了一首表白的詩。 草叢張滿虛松的耳孔偷聽, 燕子呢喃著害羞沖上樹頂 我該躲哪里呀,就去到你心底, 那里鮮...
    稻粱閱讀 349評論 0 6

友情鏈接更多精彩內(nèi)容