K線開發(fā)之K線整體搭建


前言

當寫完了所有需要使用的素材類后,我們開始搭建一個比較完整的K線Demo。

它包含以下功能:

1、可以展示蠟燭圖
2、可以展示OHLC圖
3、可以左右滑動
4、可以長按出現(xiàn)十字叉
5、有基本的價格和日期區(qū)間展示

GO

在上幾篇文章中,我們已經知道如何繪制蠟燭、邊框、OHLC等。所以在這里,可以直接使用已經寫好的類。

繪制邊框

首頁,我們先繪制一個包含主副圖的邊框:

/**
 繪制邊框
 */
- (void)drawBorder
{
    //設置主圖、主圖指標、副圖、副圖指標rect
    _mainIndexRect = CGRectMake(0, 0, CGRectGetWidth(self.frame), mainIndexH);
    _mainRect = CGRectMake(0, mainIndexH, CGRectGetWidth(self.frame), (CGRectGetHeight(self.frame) - (mainIndexH + accessoryIndexH + dateH)) * mainFrameScale);
    _accessoryIndexRect = CGRectMake(0, mainIndexH + CGRectGetHeight(_mainRect)+dateH, CGRectGetWidth(self.frame), accessoryIndexH);
    _accessoryRect = CGRectMake(0, mainIndexH + CGRectGetHeight(_mainRect)+dateH+accessoryIndexH, CGRectGetWidth(self.frame), (CGRectGetHeight(self.frame) - (mainIndexH + accessoryIndexH + dateH)) * (1-mainFrameScale));
    
    
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.bounds];
    
    [path moveToPoint:CGPointMake(0, mainIndexH)];
    [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), mainIndexH)];
    
    [path moveToPoint:CGPointMake(0, CGRectGetMaxY(_mainRect))];
    [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetMaxY(_mainRect))];
    
    [path moveToPoint:CGPointMake(0, CGRectGetMinY(_accessoryIndexRect))];
    [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetMinY(_accessoryIndexRect))];
    
    [path moveToPoint:CGPointMake(0, CGRectGetMinY(_accessoryRect))];
    [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetMinY(_accessoryRect))];
    
    float mainUnitH = CGRectGetHeight(_mainRect) / 4.f;
    float mainUnitW = CGRectGetWidth(_mainRect) / 4.f;
    
    for (int idx = 1; idx <= 3; idx++)
    {
        //畫3條橫線
        [path moveToPoint:CGPointMake(0, mainIndexH + mainUnitH * idx)];
        [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), mainIndexH + mainUnitH * idx)];
        
        //畫3條豎線
        [path moveToPoint:CGPointMake(idx * mainUnitW, mainIndexH)];
        [path addLineToPoint:CGPointMake(idx * mainUnitW, CGRectGetMaxY(_mainRect))];
        
        //畫3條豎線
        [path moveToPoint:CGPointMake(idx * mainUnitW, CGRectGetMinY(_accessoryRect))];
        [path addLineToPoint:CGPointMake(idx * mainUnitW, CGRectGetMaxY(_accessoryRect))];
    }

    float accessoryUnitH = CGRectGetHeight(_accessoryRect) / 2.f;
    [path moveToPoint:CGPointMake(0, CGRectGetMaxY(_accessoryRect) - accessoryUnitH)];
    [path addLineToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetMaxY(_accessoryRect) - accessoryUnitH)];
    
    borderLayer.path = path.CGPath;
    borderLayer.lineWidth = 0.5f;
    borderLayer.strokeColor = [UIColor colorWithRed:222.f/255.f green:222.f/255.f blue:222.f/255.f alpha:1.f].CGColor;
    borderLayer.fillColor = [UIColor clearColor].CGColor;
    
    [self.layer addSublayer:borderLayer];
    
}

效果如下:


邊框

導入數(shù)據(jù)

邊框繪制完以后,我們把k線數(shù)據(jù)轉換為坐標點。在轉換之前,還需要尋找當前在屏幕上展示數(shù)據(jù)的極限值,也就是最大最小值。

這里要注意,因為當前屏幕中一般最多顯示60個左右的蠟燭數(shù)量,但是某一個周期K線的蠟燭數(shù)量一般是幾百個,所以在K線數(shù)據(jù)轉為模型數(shù)據(jù)后,需要有一個起始索引來標識當前展示的數(shù)據(jù)范圍。

尋找極限值:

//求出最大最小值
    _min = (float)INT32_MAX;
    _max = (float)INT32_MIN;
    for (int idx=_startIndex; idx<_endIndex; idx++)
    {
        YKKLineModel *model = self.kLineModelArr[idx];
        if (_min > model.low)
        {
            _min = model.low;
        }
        if (_max < model.high)
        {
            _max = model.high;
        }
    }

把數(shù)據(jù)轉換為坐標點:

    [self.displayPointArr removeAllObjects];
    //每根蠟燭的寬度
    float candleW = CGRectGetWidth(_mainRect) / candleCount;
    for (int idx = _startIndex; idx<_endIndex; idx++)
    {
        YKKLineModel *model = self.kLineModelArr[idx];
        float x = CGRectGetMinX(_mainRect) + candleW * (idx - (_startIndex - 0));
     
        CGPoint hPoint = CGPointMake(x + candleW/2,
                                     ABS(CGRectGetMaxY(_mainRect) - (model.high  - _min)/unitValue));
        CGPoint lPoint = CGPointMake(x + candleW/2,
                                     ABS(CGRectGetMaxY(_mainRect) - (model.low   - _min)/unitValue));
        CGPoint oPoint = CGPointMake(x + candleW/2,
                                     ABS(CGRectGetMaxY(_mainRect) - (model.open  - _min)/unitValue));
        CGPoint cPoint = CGPointMake(x + candleW/2,
                                     ABS(CGRectGetMaxY(_mainRect) - (model.close - _min)/unitValue));
        [_displayPointArr addObject:[YKCandlePointModel candlePointModelWithOpoint:oPoint
                                                                            Hpoint:hPoint
                                                                            Lpoint:lPoint
                                                                            Cpoint:cPoint]];
    }

繪制蠟燭/OHLC

導入數(shù)據(jù)并且轉換為坐標點以后,接下來開始繪制。

繪制蠟燭:

    //每根蠟燭的寬度
    float candleW = CGRectGetWidth(_mainRect) / candleCount;
    
    for (int idx = 0; idx< candleCount; idx++)
    {
        YKCandlePointModel *model = pointModelArr[idx];
        CAShapeLayer *layer = [CAShapeLayer getCandleLayerWithPointModel:model candleW:candleW];
        
        [self.candleLayer addSublayer:layer];
    }
    
    [self.layer addSublayer:self.candleLayer];

繪制OHLC:

    //每根OHLC的寬度
    float candleW = CGRectGetWidth(_mainRect) / candleCount;
    
    for (int idx = 0; idx< candleCount; idx++)
    {
        YKCandlePointModel *model = pointModelArr[idx];
        CAShapeLayer *layer = [CAShapeLayer getOHLCLayerWithPointModel:model candleW:candleW];
        
        [self.ohlcLayer addSublayer:layer];
    }
    
    [self.layer addSublayer:self.ohlcLayer];

效果如下:

蠟燭
OHLC

繪制價格、日期區(qū)間

蠟燭繪制完以后,我們還剩下價格、日期。價格是指左側的5個價格的區(qū)間標識,從上往下降序排列;日期為下方5個日期的區(qū)間標識,每一個標識都是距離當前點最近的點的日期。

繪制左側價格:

    float unitPrice = (_max - _min) / 4.f;
    float unitH = CGRectGetHeight(_mainRect) / 4.f;
    
    //求得價格rect
    NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:9.f]};
    CGRect priceRect = [self rectOfNSString:[NSString stringWithFormat:@"%.2f", _max] attribute:attribute];
    
    for (int idx = 0; idx < 5; idx++)
    {
        float height = 0.f;
        if (idx == 4)
        {
            height = idx * unitH - CGRectGetHeight(priceRect);
        } else
        {
            height = idx * unitH;
        }
        CGRect rect = CGRectMake(CGRectGetMinX(_mainRect),
                                 CGRectGetMinY(_mainRect) + height,
                                 CGRectGetWidth(priceRect),
                                 CGRectGetHeight(priceRect));
        //計算價格
        NSString *str = [NSString stringWithFormat:@"%.2f", _max - idx * unitPrice];
        CATextLayer *layer = [CATextLayer getTextLayerWithString:str
                                                       textColor:[UIColor blackColor]
                                                        fontSize:9.f
                                                 backgroundColor:[UIColor clearColor]
                                                           frame:rect];
        
        [self.leftPriceLayer addSublayer:layer];
    }
    
    [self.layer addSublayer:self.leftPriceLayer];

繪制日期(這里要注意,因為使用的Demo數(shù)據(jù)周期為天,所以日期也具體到天):

    NSMutableArray *kLineDateArr = [NSMutableArray array];
    int unitCount = candleCount / 4;
    for (int idx=0; idx<5; idx++)
    {
        YKKLineModel *model = self.kLineModelArr[_startIndex + idx * unitCount];
        
        NSDate *detaildate = [NSDate dateWithTimeIntervalSince1970:model.timeStamp];
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"YYYY-MM-dd"];
        NSString *dateStr = [dateFormatter stringFromDate:detaildate];
        
        [kLineDateArr addObject:dateStr];
    }
    
    NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:9.f]};
    CGRect strRect = [self rectOfNSString:@"0000-00-00" attribute:attribute];
    float strW = CGRectGetWidth(strRect);
    float strH = CGRectGetHeight(strRect);
    
    float unitW = CGRectGetWidth(_mainRect) / 4;
    
    //循環(huán)繪制坐標點
    for (int idx = 0; idx < kLineDateArr.count; idx++)
    {
        CATextLayer *textLayer = nil;
        
        if (idx == kLineDateArr.count-1)
        {//最后一個
            CGRect rect = CGRectMake(idx * unitW - strW, CGRectGetMaxY(_mainRect), strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:kLineDateArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }else if(idx == 0)
        {//第一個
            CGRect rect = CGRectMake(idx * unitW, CGRectGetMaxY(_mainRect), strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:kLineDateArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }else
        {//中間
            CGRect rect = CGRectMake(idx * unitW - strW/2, CGRectGetMaxY(_mainRect), strW, strH);
            textLayer = [CATextLayer getTextLayerWithString:kLineDateArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
        }
        
        [self.bottomDateLayer addSublayer:textLayer];
    }
    
    [self.layer addSublayer:self.bottomDateLayer];

效果如下:

區(qū)間標識

添加左右滑動

在以前我們討論過滑動偏移量的獲取方式,這里就暫且通過添加手勢來獲取偏移量。使用長按手勢來做十字叉效果,使用拖動手勢來做左右滑動效果。

當檢測到用戶長按時,獲取坐標點然后轉換為柱子索引,再繪制十字叉,左側和下側展示當前索引的數(shù)據(jù)。當用戶抬起手指時,可以選擇及時清除掉十字叉,也可以加一個延時清除。

當檢測到用戶拖動時,用偏移量的正負來判斷用戶是向左還是向右拖動。每次檢測到拖動后,獲取到偏移量,因為這個偏移量不是太線性,所以添加一個范圍的判斷。拿到偏移量以后,再更新展示數(shù)據(jù)的起始索引值,然后再更新視圖。

添加手勢:

    //添加左右拖動手勢
    UIPanGestureRecognizer *panG = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureAction:)];
    [_kLineView addGestureRecognizer:panG];
    
    //添加長按手勢
    UILongPressGestureRecognizer *longG = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(kLineLongGestureAction:)];
    longG.minimumPressDuration = 0.5f;
    longG.numberOfTouchesRequired = 1;
    [_kLineView addGestureRecognizer:longG];

響應手勢:

/**
 K線響應長按手勢
 
 @param longGesture 手勢對象
 */
- (void)kLineLongGestureAction:(UILongPressGestureRecognizer *)longGesture
{
    if (longGesture.state == UIGestureRecognizerStateBegan || longGesture.state == UIGestureRecognizerStateChanged)
    {
        CGPoint point = [longGesture locationInView:_kLineView];
        
        float x = 0.f;
        if (point.x < 0.f)
        {
            x = 0.f;
        }else if (point.x > CGRectGetWidth(_kLineView.frame))
        {
            x = CGRectGetWidth(_kLineView.frame)-1;
        }else
        {
            x = point.x;
        }
        //當長按滑動時,每滑動一次話會重新刷新十字叉
        [_kLineView drawCrossViewWithX:x];
    }else
    {
        //當手指抬起時,及時把十字叉取消掉
        [_kLineView clearCrossViewLayer];
    }
}

/**
 響應拖動手勢
 
 @param panGesture 手勢對象
 */
- (void)panGestureAction:(UIPanGestureRecognizer *)panGesture
{
    CGPoint point = [panGesture translationInView:_kLineView];
    float offset =  point.x - kLineGlobalOffset;
    if (panGesture.state == UIGestureRecognizerStateChanged && ABS(offset) > 3)
    {
        if (offset > 0)
        {
            if (ABS(offset) > 20)
            {
                [_kLineView dragRightOffsetcount:5];
                
            } else if(ABS(offset) > 6)
            {
                [_kLineView dragRightOffsetcount:2];
                
            } else
            {
                [_kLineView dragRightOffsetcount:1];
            }
        }else
        {
            if (ABS(offset) > 20)
            {
                [_kLineView dragLeftOffsetcount:5];
                
            } else if(ABS(offset) > 6)
            {
                [_kLineView dragLeftOffsetcount:2];
                
            } else
            {
                [_kLineView dragLeftOffsetcount:1];
            }
        }
        kLineGlobalOffset = point.x;
    }
    
    if (panGesture.state == UIGestureRecognizerStateEnded ||
        panGesture.state == UIGestureRecognizerStateCancelled ||
        panGesture.state == UIGestureRecognizerStateFailed)
    {
        kLineGlobalOffset= 0.f;
    }
}

效果如下:

OHLC線

Demo源碼下載

至此,我們已經把K線主圖大部分的功能搭建完畢。Demo源碼點擊這里下載。下篇文章,會說到主副圖指標的一些事,敬請期待。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,323評論 25 708
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,650評論 4 61
  • 深夜十二點,白蕓回到家,臉上掛著淚痕,男友還在呼呼大睡,躡手躡腳走到浴室。 浴室水聲淅淅瀝瀝,她用香皂使勁擦洗身子...
    唯美成殤閱讀 38,278評論 0 0
  • 故鄉(xiāng)是一個很難去提筆書寫的命題,總是帶給人一種忽明忽暗忽遠忽近的迷離感。到現(xiàn)在為止,我對故鄉(xiāng)的情感總是有一些淡淡的...
    冬肆閱讀 302評論 0 0
  • 花王泡沫染發(fā)劑新手使用后效果 如果你想染發(fā)但又沒什么錢去理發(fā)店染,那你可以動手自己試試,絕對不會讓你失望的。 下面...
    果果99閱讀 79,727評論 0 5

友情鏈接更多精彩內容