热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

IOS端K线系列之分时图-整体搭建

k线系列目录查看目录请点击这儿提出问题记得曾经有一个脑筋急转弯:问:如何把大象装进冰箱里?答:总共分三步(1)把冰箱门开起来(2)把大象推进去(3)把冰箱

k线系列目录

查看目录请点击这儿

提出问题

记得曾经有一个脑筋急转弯:

问:如何把大象装进冰箱里?
答:总共分三步(1)把冰箱门开起来(2)把大象推进去(3)把冰箱门关上

虽然是一个笑话,但是从另一种角度来讲也是一种解决问题的思路。2008年北京奥运会作为一个大型项目,时长持续8年之久,可是项目的过程也仅仅只分为五个过程组:

(1)启动过程组(2)规划过程组(3)执行过程组(4)监控过程组(5)收尾过程组

那对于我们的主题:分时图,其实也可以这样看待。如何绘制一张分时图,如果你看过前面几篇的文章(没看过的点这儿),心里应该会有个大体的过程:

  1. 绘制分时图的边框
  2. 绘制分时图的X轴时间点
  3. 绘制价格区间标识
  4. 绘制分时线
  5. 绘制均线
  6. 绘制呼吸灯

完成6个步骤,那一个分时图就已经绘制完成。

开始搞起

有了思路,就开始干活!虽然在前几篇文章中有说过一些绘制的方法,这里就再说一次,权当复习。

(一)绘制分时图边框

要实现的效果是一个6 * 4 方格的边框,如下图:

分时图边框

那就是说,我们分两个for循环,来完成横向7条线、竖向5条线的绘制工作。上代码!

    CGRect rect = CGRectMake(frameX, frameY, frameW, frameH);
UIBezierPath *framePath = [UIBezierPath bezierPathWithRect:rect];
CAShapeLayer *layer = [CAShapeLayer layer];

float unitW = frameW/6;
float unitH = frameH/4;
//绘制7条竖线
for (int idx=0; idx<7; idx++)
{
CGPoint startPoint = CGPointMake(frameX + unitW * idx, frameY);
CGPoint endPoint = CGPointMake(frameX + unitW * idx, frameY + frameH);
[framePath moveToPoint:startPoint];
[framePath addLineToPoint:endPoint];
}
//绘制5条横线
for (int idx=0; idx<5; idx++)
{
CGPoint startPoint = CGPointMake(frameX, frameY + unitH * idx);
CGPoint endPoint = CGPointMake(frameX + frameW, frameY + unitH * idx);
[framePath moveToPoint:startPoint];
[framePath addLineToPoint:endPoint];
}
//设置图层的属性
layer.path = framePath.CGPath;
layer.lineWidth = 0.5f;
layer.strokeColor = [UIColor colorWithRed:220.f/255.f green:220.f/255.f blue:220.f/255.f alpha:1.f].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;

(二)绘制分时图的X轴时间点

框绘制完成以后,就开始绘制边框最下方的时间点,由于现货类K线框架默认交易时间为24小时,所以时间也设置为6.01至6.00。这样的话,每4个小时一个方格,共需要绘制7个时间点。代码如下:

    //坐标点数组
NSArray *timePointArr = @[@"06:01", @"10:00", @"14:00", @"18:00", @"22:00", @"02:00", @"06:00"];
NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:9.f]};
CGRect strRect = [self rectOfNSString:@"00:00" attribute:attribute];
float strW = CGRectGetWidth(strRect);
float strH = CGRectGetHeight(strRect);

float unitW = CGRectGetWidth(self.frame) / 6;
//循环绘制坐标点
for (int idx = 0; idx .count; idx++)
{
CATextLayer *textLayer = nil;

if (idx == timePointArr.count-1)
{//最后一个
CGRect rect = CGRectMake(idx * unitW - strW, CGRectGetHeight(self.frame)-timePointH, strW, strH);
textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
}else if(idx == 0)
{//第一个
CGRect rect = CGRectMake(idx * unitW, CGRectGetHeight(self.frame)-timePointH, strW, strH);
textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
}else
{//中间
CGRect rect = CGRectMake(idx * unitW - strW/2, CGRectGetHeight(self.frame)-timePointH, strW, strH);
textLayer = [CATextLayer getTextLayerWithString:timePointArr[idx] textColor:[UIColor blackColor] fontSize:9.f backgroundColor:[UIColor clearColor] frame:rect];
}

[self.layer addSublayer:textLayer];
}

(三)绘制价格区间标识

到现在,边框和时间点绘制完成。接下来,再绘制价格区间。价格区间的求法是先要出极限值,也就是最大值最小值,然后按下列的规则得出:

if(ABS(当前分时线中最大值 - 昨日收盘价)) >= (ABS(昨日收盘价-当前分时线中最小值))
{
最上侧价格 = 当前分时线中最大值;
最下侧价格 = 昨日收盘价 - ABS(当前分时线中最大值 - 昨日收盘价);
}else
{
最上侧价格 = 昨日收盘价 + ABS(昨日收盘价-当前分时线中最小值);
最下侧价格 = 当前分时线中最小值;
}

代码如下:

    //循环绘制5行数据
//左边是价格 右边是百分比
for (int idx = 0; idx <5; idx++)
{
float height = 0.f;
if (idx == 4)
{
height = idx * unitH - CGRectGetHeight(priceRect);
} else
{
height = idx * unitH;
}
CGRect leftRect = CGRectMake(0,
height,
CGRectGetWidth(priceRect),
CGRectGetHeight(priceRect));
CGRect rightRect = CGRectMake(CGRectGetMaxX(self.frame)-CGRectGetWidth(perRect)-14,
height,
CGRectGetWidth(perRect),
CGRectGetHeight(perRect));
//计算价格和百分比
NSString *leftStr = [NSString stringWithFormat:@"%.2f", self.maxValue - idx * unitPrice];
NSString *rightStr = [NSString stringWithFormat:@"%.2f%%", (self.maxValue - idx * unitPrice - self.yc)/self.yc];

CATextLayer *leftLayer = [CATextLayer getTextLayerWithString:leftStr
textColor:[UIColor blackColor]
fontSize:9.f
backgroundColor:[UIColor clearColor]
frame:leftRect];
CATextLayer *rightLayer = [CATextLayer getTextLayerWithString:rightStr
textColor:[UIColor blackColor]
fontSize:9.f
backgroundColor:[UIColor clearColor]
frame:rightRect];

[self.layer addSublayer:leftLayer];
[self.layer addSublayer:rightLayer];
}

(四)绘制分时线

左右的价格区间绘制完以后,接下来是绘制分时线。

1、这里要注意,因为默认是24小时的交易时间,那分时线是每一个点为一分钟,24小时换算成分钟是1440分钟。

2、使用边框的宽 除以 1440,就可以得出每一个点做占的宽,这样在转换每一个分时点的坐标时,x值就可以使用这个宽得出。

3、那每一个分时点的y值是如何求出的?是先用最大值减去最小值得出边框高所对应的值,然后用这个值除以边框高,就得出单位值所对应的高,那求y值时就可以直接用这个值。

转换代码如下:

    CGFloat unitW = CGRectGetWidth(self.frame) / 1440;
CGFloat unitValue = (self.maxValue - self.minValue) / (CGRectGetHeight(self.frame) - timePointH);

NSMutableArray *pointArr = [NSMutableArray array];
//遍历数据模型
[self.timeCharModelArr enumerateObjectsUsingBlock:^(YKTimeChartModel * _Nonnull model, NSUInteger idx, BOOL * _Nonnull stop) {

CGFloat x = idx * unitW;
//生成分时线坐标点
CGPoint linePoint = CGPointMake(x, ABS(CGRectGetMaxY(self.frame) - timePointH) - (model.clp - self.minValue)/ unitValue);
//生成均线坐标点
CGPoint avgPoint = CGPointMake(x, ABS(CGRectGetMaxY(self.frame) - timePointH) - (model.avp - self.minValue)/ unitValue);

YKTimeLinePointModel *pointModel = [YKTimeLinePointModel new];
pointModel.linePoint = linePoint;
pointModel.avgPoint = avgPoint;
[pointArr addObject:pointModel];
}];

return pointArr;

那每一个分时线的点的坐标全部转换完以后,我们就可以直接遍历这个数组来循环绘制了。上代码:

    //绘制分时线
YKTimeLinePointModel *firstModel = pointArr.firstObject;
[timeLinePath moveToPoint:firstModel.linePoint];
for (int i=1; i
{
YKTimeLinePointModel *model = pointArr[i];
[timeLinePath addLineToPoint:model.linePoint];
}
lineLayer.path = timeLinePath.CGPath;
lineLayer.lineWidth = 0.4f;
lineLayer.strokeColor = [UIColor colorWithRed:100.f/255.f green:149.f/255.f blue:237.f/255.f alpha:1.f].CGColor;
lineLayer.fillColor = [UIColor clearColor].CGColor;

//绘制背景区域
YKTimeLinePointModel *lastModel = [pointArr lastObject];
[timeLinePath addLineToPoint:CGPointMake(lastModel.linePoint.x, CGRectGetHeight(self.frame) - timePointH)];
[timeLinePath addLineToPoint:CGPointMake(firstModel.linePoint.x, CGRectGetHeight(self.frame)- timePointH)];
fillLayer.path = timeLinePath.CGPath;
fillLayer.fillColor = [UIColor colorWithRed:135.f/255.f green:206.f/255.f blue:250.f/255.f alpha:0.5f].CGColor;
fillLayer.strokeColor = [UIColor clearColor].CGColor;
fillLayer.zPosition -= 1;

(五)绘制均线

分时线绘制完以后,接着绘制均线,也就是那根黄色的线:

    CAShapeLayer *avgLineLayer = [CAShapeLayer layer];

UIBezierPath *avgLinePath = [UIBezierPath bezierPath];
YKTimeLinePointModel *firstModel = pointArr.firstObject;
[avgLinePath moveToPoint:firstModel.avgPoint];

for (int i=1; i
{
YKTimeLinePointModel *model = pointArr[i];
[avgLinePath addLineToPoint:model.avgPoint];
}

avgLineLayer.path = avgLinePath.CGPath;
avgLineLayer.lineWidth = 2.f;
avgLineLayer.strokeColor = [UIColor colorWithRed:255.f/255.f green:215.f/255.f blue:0.f/255.f alpha:1.f].CGColor;
avgLineLayer.fillColor = [UIColor clearColor].CGColor;

至此,我们已经把分时图绘制完成,来看看我们的成果吧!

分时图

(六)绘制呼吸灯

怎么样? 是不是感觉很棒?不过刚才差点忘了一个元素,就是呼吸灯效果,它可是最能反映我们的分时图动态效果的地方。

那接着上代码吧!(如果对CABasicAnimation不是太了解的话,点击这儿,有详细介绍)

/**
绘制呼吸灯
*/

- (void)drawBreathingLightWithPoint:(CGPoint)point
{
CALayer *layer = [CALayer layer];
//设置任意位置
layer.frame = CGRectMake(point.x, point.y, 3, 3);
//设置呼吸灯的颜色
layer.backgroundColor = [UIColor blueColor].CGColor;
//设置好半径
layer.cornerRadius = 1.5;
//给当前图层添加动画组
[layer addAnimation:[self createBreathingLightAnimationWithTime:2] forKey:nil];

[self.layer addSublayer:layer];
}


/**
生成动画

@param time 动画单词持续时间
@return 返回动画组
*/

- (CAAnimationGroup *)createBreathingLightAnimationWithTime:(double)time
{
//实例化CABasicAnimation
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
//从1开始
scaleAnimation.fromValue = @1;
//到3.5
scaleAnimation.toValue = @3.5;
//结束后不执行逆动画
scaleAnimation.autoreverses = NO;
//无限循环
scaleAnimation.repeatCount = HUGE_VALF;
//一次执行time秒
scaleAnimation.duration = time;
//结束后从渲染树删除,变回初始状态
scaleAnimation.removedOnCompletion= YES;
scaleAnimation.fillMode = kCAFillModeForwards;

CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.fromValue = @1.0;
opacityAnimation.toValue = @0;
opacityAnimation.autoreverses = NO;
opacityAnimation.repeatCount = HUGE_VALF;
opacityAnimation.duration = time;
opacityAnimation.removedOnCompletion= YES;
opacityAnimation.fillMode = kCAFillModeForwards;

CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = time;
group.autoreverses = NO;
group.animatiOns= @[scaleAnimation, opacityAnimation];
group.repeatCount = HUGE_VALF;
//这里也应该设置removedOnCompletion和fillMode属性,以具体情况而定

return group;
}

最终的效果如下(特意上传了一个gif图):

分时图

好啦,我们的分时图最终大功告成。但先别急着太高兴,其实还有很多欠缺的地方,比如:

  • 主题、颜色是不是需要可配置?
  • 参数、大小是不是需要动态自适应?
  • 边框线段是不是要可自定义?比如来个其他颜色的虚线?
  • 这可是现货类?股票类的X轴怎么修改?
  • demo的源码如何修改变成强扩展的框架的一部分呢?
  • …….

还有很多地方、很多效果值得我们去细细打磨它!如果有需要讨论的地方,随时欢迎拍砖灌水!

最后献上demo源码一份,拿走不谢!点这里。


推荐阅读
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
author-avatar
lb小小凡人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有