前言
當(dāng)寫完了所有需要使用的素材類后乏屯,我們開始搭建一個(gè)比較完整的K線Demo漾月。
它包含以下功能:
1病梢、可以展示蠟燭圖
2、可以展示OHLC圖
3梁肿、可以左右滑動(dòng)
4蜓陌、可以長(zhǎng)按出現(xiàn)十字叉
5、有基本的價(jià)格和日期區(qū)間展示
GO
在上幾篇文章中吩蔑,我們已經(jīng)知道如何繪制蠟燭钮热、邊框、OHLC等烛芬。所以在這里隧期,可以直接使用已經(jīng)寫好的類。
繪制邊框
首頁赘娄,我們先繪制一個(gè)包含主副圖的邊框:
/**
繪制邊框
*/
- (void)drawBorder
{
//設(shè)置主圖仆潮、主圖指標(biāo)、副圖遣臼、副圖指標(biāo)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];
}
效果如下:
導(dǎo)入數(shù)據(jù)
邊框繪制完以后性置,我們把k線數(shù)據(jù)轉(zhuǎn)換為坐標(biāo)點(diǎn)。在轉(zhuǎn)換之前揍堰,還需要尋找當(dāng)前在屏幕上展示數(shù)據(jù)的極限值鹏浅,也就是最大最小值。
這里要注意屏歹,因?yàn)楫?dāng)前屏幕中一般最多顯示60個(gè)左右的蠟燭數(shù)量篡石,但是某一個(gè)周期K線的蠟燭數(shù)量一般是幾百個(gè),所以在K線數(shù)據(jù)轉(zhuǎn)為模型數(shù)據(jù)后西采,需要有一個(gè)起始索引來標(biāo)識(shí)當(dāng)前展示的數(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ù)轉(zhuǎn)換為坐標(biāo)點(diǎn):
[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
導(dǎo)入數(shù)據(jù)并且轉(zhuǎn)換為坐標(biāo)點(diǎn)以后继控,接下來開始繪制械馆。
繪制蠟燭:
//每根蠟燭的寬度
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];
效果如下:
繪制價(jià)格、日期區(qū)間
蠟燭繪制完以后武通,我們還剩下價(jià)格霹崎、日期。價(jià)格是指左側(cè)的5個(gè)價(jià)格的區(qū)間標(biāo)識(shí)冶忱,從上往下降序排列尾菇;日期為下方5個(gè)日期的區(qū)間標(biāo)識(shí),每一個(gè)標(biāo)識(shí)都是距離當(dāng)前點(diǎn)最近的點(diǎn)的日期。
繪制左側(cè)價(jià)格:
float unitPrice = (_max - _min) / 4.f;
float unitH = CGRectGetHeight(_mainRect) / 4.f;
//求得價(jià)格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));
//計(jì)算價(jià)格
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];
繪制日期(這里要注意派诬,因?yàn)槭褂玫腄emo數(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)繪制坐標(biāo)點(diǎn)
for (int idx = 0; idx < kLineDateArr.count; idx++)
{
CATextLayer *textLayer = nil;
if (idx == kLineDateArr.count-1)
{//最后一個(gè)
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)
{//第一個(gè)
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];
效果如下:
添加左右滑動(dòng)
在以前我們討論過滑動(dòng)偏移量的獲取方式,這里就暫且通過添加手勢(shì)來獲取偏移量默赂。使用長(zhǎng)按手勢(shì)來做十字叉效果沛鸵,使用拖動(dòng)手勢(shì)來做左右滑動(dòng)效果。
當(dāng)檢測(cè)到用戶長(zhǎng)按時(shí)缆八,獲取坐標(biāo)點(diǎn)然后轉(zhuǎn)換為柱子索引曲掰,再繪制十字叉,左側(cè)和下側(cè)展示當(dāng)前索引的數(shù)據(jù)奈辰。當(dāng)用戶抬起手指時(shí)栏妖,可以選擇及時(shí)清除掉十字叉,也可以加一個(gè)延時(shí)清除奖恰。
當(dāng)檢測(cè)到用戶拖動(dòng)時(shí)吊趾,用偏移量的正負(fù)來判斷用戶是向左還是向右拖動(dòng)。每次檢測(cè)到拖動(dòng)后房官,獲取到偏移量趾徽,因?yàn)檫@個(gè)偏移量不是太線性,所以添加一個(gè)范圍的判斷翰守。拿到偏移量以后孵奶,再更新展示數(shù)據(jù)的起始索引值,然后再更新視圖蜡峰。
添加手勢(shì):
//添加左右拖動(dòng)手勢(shì)
UIPanGestureRecognizer *panG = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureAction:)];
[_kLineView addGestureRecognizer:panG];
//添加長(zhǎng)按手勢(shì)
UILongPressGestureRecognizer *longG = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(kLineLongGestureAction:)];
longG.minimumPressDuration = 0.5f;
longG.numberOfTouchesRequired = 1;
[_kLineView addGestureRecognizer:longG];
響應(yīng)手勢(shì):
/**
K線響應(yīng)長(zhǎng)按手勢(shì)
@param longGesture 手勢(shì)對(duì)象
*/
- (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;
}
//當(dāng)長(zhǎng)按滑動(dòng)時(shí)了袁,每滑動(dòng)一次話會(huì)重新刷新十字叉
[_kLineView drawCrossViewWithX:x];
}else
{
//當(dāng)手指抬起時(shí),及時(shí)把十字叉取消掉
[_kLineView clearCrossViewLayer];
}
}
/**
響應(yīng)拖動(dòng)手勢(shì)
@param panGesture 手勢(shì)對(duì)象
*/
- (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;
}
}
效果如下:
Demo源碼下載
至此湿颅,我們已經(jīng)把K線主圖大部分的功能搭建完畢载绿。Demo源碼點(diǎn)擊這里下載。下篇文章油航,會(huì)說到主副圖指標(biāo)的一些事崭庸,敬請(qǐng)期待。