iOS雷达图 iOS RadarChart实现

实现效果
刚拿到设计稿的时候大概看了一眼,当时心里想着放张背景图,然后计算下相应点的坐标,在最上面画一层就OK了,其实一开始实现的时候也确实是这么做的,然后我就日了狗了,发现设计稿上多层五边形的间隔不是相等的,也就是说继续按照之前的想法进行实现就要计算出每层顶点的坐标,那样的话代码估计会被坐标值霸屏了。好吧,推倒重来。
一层一层的分析这个需求,首先是五边形的绘制,我创建了一个UIBezierPath的category。具体的代码如下,其中第一个方法是用来画各顶点不规律的五边形的,而第二个方法是用来画那几个背景五边形,两个方法中的length都指的的中心点到各顶点的距离,第三个方法则是用来将距离转换成具体坐标。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
+ (CGPathRef)drawPentagonWithCenter:(CGPoint)center Length:(double)length{ NSArray *lengths = [NSArray arrayWithObjects:@(length),@(length),@(length),@(length),@(length), nil]; return [self drawPentagonWithCenter:center LengthArray:lengths];}+ (CGPathRef)drawPentagonWithCenter:(CGPoint)center LengthArray:(NSArray *)lengths{ NSArray *coordinates = [self converCoordinateFromLength:lengths Center:center]; UIBezierPath *bezierPath = [UIBezierPath bezierPath]; for (int i = 0; i < [coordinates count]; i++) { CGPoint point = [[coordinates objectAtIndex:i] CGPointValue]; if (i == 0) { [bezierPath moveToPoint:point]; } else { [bezierPath addLineToPoint:point]; } } [bezierPath closePath]; return bezierPath.CGPath;}+ (NSArray *)converCoordinateFromLength:(NSArray *)lengthArray Center:(CGPoint)center{ NSMutableArray *coordinateArray = [NSMutableArray array]; for (int i = 0; i < [lengthArray count] ; i++) { double length = [[lengthArray objectAtIndex:i] doubleValue]; CGPoint point = CGPointZero; if (i == 0) { point = CGPointMake(center.x - length * sin(M_PI / 5.0), center.y - length * cos(M_PI / 5.0)); } else if (i == 1) { point = CGPointMake(center.x + length * sin(M_PI / 5.0), center.y - length * cos(M_PI / 5.0)); } else if (i == 2) { point = CGPointMake(center.x + length * cos(M_PI / 10.0), center.y + length * sin(M_PI / 10.0)); } else if (i == 3) { point = CGPointMake(center.x, center.y +length); } else { point = CGPointMake(center.x - length * cos(M_PI / 10.0), center.y + length * sin(M_PI / 10.0)); } [coordinateArray addObject:[NSValue valueWithCGPoint:point]]; } return coordinateArray;} |
至于最顶层数据五边形的动画绘制,我做了两种实现(因为他们也还没确定用哪个),额,怎么解释两者的区别呢,一种是按照与各边成比例的速度放大,一种是按照各边同样的速度放大。两种方法我都放上来:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
#pragma mark - 描绘分数五边行 按照与各边成比例的速度放大- (void)drawScorePentagonV{ NSArray *lengthsArray = [self convertLengthsFromScore:self.scoresArray]; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; pathAnimation.fromValue = (id)[UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 100.0) Length:0]; pathAnimation.toValue = (id)[UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 100.0) LengthArray:lengthsArray]; pathAnimation.duration = 0.75; pathAnimation.autoreverses = NO; pathAnimation.repeatCount = 0; pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; [self.shapeLayer addAnimation:pathAnimation forKey:@"scale"]; self.shapeLayer.path = [UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 100.0) LengthArray:lengthsArray]; [self.layer addSublayer:self.shapeLayer]; [self performSelector:@selector(changeBgSizeFinish) withObject:nil afterDelay:0.75];}#pragma mark - 描绘分数五边行 按照各边同样的速度放大- (void)drawScorePentagonV{ NSArray *scoresArray = [self analysisScoreArray:self.scoresArray]; NSMutableArray *lengthsArray = [NSMutableArray array]; [lengthsArray addObject:(id)[UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 231 / 2.0) Length:0.0]]; for (int i = 0; i < [scoresArray count]; i++) { NSArray *scores = [scoresArray objectAtIndex:i]; [lengthsArray addObject:(id)[UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 231 / 2.0) LengthArray:[self convertLengthsFromScore:scores]]]; } CAKeyframeAnimation *frameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"path"]; frameAnimation.values = lengthsArray; frameAnimation.keyTimes = [self analysisDurationArray:self.scoresArray]; frameAnimation.duration = 2; frameAnimation.calculationMode = kCAAnimationLinear; [self.shapeLayer addAnimation:frameAnimation forKey:@"scale"]; self.shapeLayer.path = [UIBezierPath drawPentagonWithCenter:CGPointMake(kRScrollAlertViewWidth / 2.0, 231 / 2.0) LengthArray:[self convertLengthsFromScore:[scoresArray lastObject]]]; [self.layer addSublayer:self.shapeLayer]; [self performSelector:@selector(changeBgSizeFinish) withObject:nil afterDelay:2];} |
接下来就是在动画结束的时候,将顶点的几个小图标加上去,没错,就是上面出现过的changeBgSizeFinish方法。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#pragma mark - 描点- (void)changeBgSizeFinish{ NSArray *array = [self convertLengthsFromScore:self.scoresArray]; NSArray *lengthsArray = [UIBezierPath converCoordinateFromLength:array Center:CGPointMake(kRScrollAlertViewWidth / 2.0, 100.0)]; for (int i = 0; i < [lengthsArray count]; i++) { CGPoint point = [[lengthsArray objectAtIndex:i] CGPointValue]; RADotView *dotV = [[RADotView alloc] init]; dotV.dotColor = [UIColor colorWithHex:0xF86465]; dotV.center = point; dotV.bounds = CGRectMake(0, 0, 8, 8); [self addSubview:dotV]; }} |
到这里整个需求就实现了,至于几个文字的Label,我没想到好的办法,都是通过量具体的坐标放到指定的位置上面的。好吧,我知道大家都很忙也比较喜欢偷懒,把剩余的相关代码也贴上来,大家也顺便帮我看看代码是否有错误的地方。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
#pragma mark - 分数转换- (NSNumber *)convertLengthFromScore:(double)score{ if (score >= 4) { return @(12 + 22 + 30 + 30); } else if (score >= 3){ return @(12 + 22 + 30 + 30 * (score - 3)); } else if (score >= 2) { return @(12 + 22 + 30 * (score - 2)); } else if (score >= 1) { return @(12 + 22 * (score - 1)); } else { return @(12 * score); }}- (NSArray *)convertLengthsFromScore:(NSArray *)scoreArray{ NSMutableArray *lengthArray = [NSMutableArray array]; for (int i = 0; i < [scoreArray count]; i++) { double score = [[scoreArray objectAtIndex:i] doubleValue]; [lengthArray addObject:[self convertLengthFromScore:score]]; } return lengthArray;}#pragma mark - 对分数进行排序(第二种动画方法需要)- (NSArray *)sortMergeScoresArray:(NSArray *)scores{ NSMutableDictionary *dic = [NSMutableDictionary dictionary]; for (int i = 0; i < [scores count]; i++) { [dic setObject:@"scoresValue" forKey:[scores objectAtIndex:i]]; } NSMutableArray *sortArray = [NSMutableArray arrayWithArray:dic.allKeys]; for (int i = 0; i < [sortArray count] - 1; i++) { for (int j = 0; j < [sortArray count] - i - 1 ; j++) { if ([[sortArray objectAtIndex:j] doubleValue] > [[sortArray objectAtIndex:j + 1] doubleValue]) { [sortArray exchangeObjectAtIndex:j withObjectAtIndex:j + 1]; } } } return sortArray;}- (NSArray *)analysisDurationArray:(NSArray *)scores{ NSMutableArray *analysisArray = [NSMutableArray array]; NSArray *sortArray = [self sortMergeScoresArray:scores]; double lastProportion = 0; [analysisArray addObject:@(0)]; for (int i = 0; i < [sortArray count]; i++) { double currentProportion = [[sortArray objectAtIndex:i] doubleValue] / [[sortArray lastObject] doubleValue]; [analysisArray addObject:@(currentProportion)]; lastProportion = currentProportion; } return analysisArray;}- (NSArray *)analysisScoreArray:(NSArray *)scores{ NSArray *sortArray = [self sortMergeScoresArray:scores]; NSMutableArray *analysisArray = [NSMutableArray array]; for (int i = 0; i < [sortArray count]; i++) { double stepScore = [[sortArray objectAtIndex:i] doubleValue]; NSMutableArray *analysisScores = [NSMutableArray array]; for (int j = 0; j < [scores count]; j++) { double score = [[scores objectAtIndex:j] doubleValue]; if (stepScore > score) { [analysisScores addObject:@(score)]; } else { [analysisScores addObject:@(stepScore)]; } } [analysisArray addObject:analysisScores]; } return analysisArray;} |
最后,总结一下这次的需求实现过程,敲代码之前一定一定要很仔细很仔细的分析一下需求,一定一定一定要。
源代码:http://download.csdn.net/detail/hbblzjy/9555333
iOS雷达图 iOS RadarChart实现的更多相关文章
- <Android 应用 之路> MPAndroidChart~BubbleChart(气泡图) and RadarChart(雷达图)
简介 MPAndroidChart是PhilJay大神给Android开发者带来的福利.MPAndroidChart是一个功能强大并且使用灵活的图表开源库,支持Android和iOS两种,这里我们暂时 ...
- iOS启动图和开屏广告图,类似网易
iOS启动图和开屏广告图,类似网易 启动图是在iOS开发过程中必不可少的一个部分,很多app在启动图之后会有一张自定义的开屏广告图,点击该广告图可以跳转到广告图对应的页面.今天呢,和大家分享一下如何添 ...
- iOS多图上传
iOS多图上传涉及到多线程问题,个人比较喜欢使用GCD操作,下边是最近写的一个多图上传代码,附带相关注释 __block BOOL allSucc = YES; __block int m = 0; ...
- iOS 1 到 iOS 10 ,我都快老了
iOS 1:iPhone诞生 虽然很难想像,但初代iPhone在问世时在功能方面其实远远落后于那时的竞争对手,比如Windows Mobile.Palm OS.塞班.甚至是黑莓.它不支持3G.多任务. ...
- IOS学习之IOS沙盒(sandbox)机制和文件操作
IOS学习之IOS沙盒(sandbox)机制和文件操作(一) 1.IOS沙盒机制 IOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都 ...
- 利用d3.js绘制雷达图
利用d3,js将数据可视化,能够做到数据与代码的分离.方便以后改动数据. 这次利用d3.js绘制了一个五维的雷达图.即将多个对象的五种属性在一张图上对照. 数据写入data.csv.数据类型写入typ ...
- IOS 动画专题 --iOS核心动画
iOS开发系列--让你的应用“动”起来 --iOS核心动画 概览 通过核心动画创建基础动画.关键帧动画.动画组.转场动画,如何通过UIView的装饰方法对这些动画操作进行简化等.在今天的文章里您可以看 ...
- 李洪强iOS开发之iOS好文章收集
李洪强iOS开发之iOS好文章收集 该文收集朋友们转发或自己的写的技术文章,如果你也有相关的好文章,欢迎留言,当好文章多的时候,我会对这些好文章进行分门别类 文章 简述 日期 直播服务配置 使用 ng ...
- R语言绘图:雷达图
使用fmsb包绘制雷达图 library("fmsb") radarfig <- rbind(rep(90, 4), rep(60, 4), c(86.17, 73.96, ...
随机推荐
- Excel 数据验证宏
Sub 宏1() ' ' 宏1 宏 ' ' With Selection.Validation .Delete .Add Type:=xlValidateList, AlertStyle:=xlVal ...
- EBS业务学习之采购管理
一.基础数据 w供应商档案 w采购员设置 w审批层次 w单据控制 w危险类代码 w检验代码 w自动来源 w供应商项目w目录册 二.业务流程 w请购单 w询价单 w报价单 w采购定单 w接收 w检验 w ...
- CSDN 支持Markdown写文章了!
开源中国等其他技术博客很早就支持markdown格式写文章了,今天发现csdn竟然也可以了,不仅支持而且可以在线预览,本地导入导出,远程导入. 这些对于程序员写东西都非常好用,不用总是花时间来排版了. ...
- 2.cocos2dx 3.2中语法的不同之处,lambada表达式的使用和function和bind函数的使用
1 打开建好的T32 Cocos2dx-3.2的一个项目 2 设置Cocos显示窗口的位置是在AppDelegate.cpp中: 3 设置自适应窗口大小的代码是在上面的 ...
- POI操作excel中的日期格式处理
转载:http://blog.csdn.net/fuxiaohui/article/details/6239925 7.3.3 POI中Excel文件Cell的类型 在读取每一个Cell的值的时候,通 ...
- ROS讲座 关于ROS2和Gazebo C++ in Open Source Robotics
分享一个不错的介绍ROS2和Gazebo的视频讲座. Gazebo中的云彩飘动起来了!!!! 超清视频分享网址: http://v.youku.com/v_show/id_XMTcyMzY0Nz ...
- 如何构建Android MVVM 应用框架
概述 说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架.然而两者的概念是不一样的,不能混为一谈.MVVM是一种架构模式,而DataBinding是 ...
- 18 UI美化状态集合的位图selector
当我们某个控件 想在不同状态下显示不同的背景图的需求 如我们需要按钮在正常状态显示一种图 按下显示另一背景图 或者单选框被选中时是一种显示图片 没选中是另一种背景图 例子 按钮在不同状态显示不同的背景 ...
- python 远程调度进程服务与客户端
python 远程调度进程服务与客户端 核心思想: 在本地或远程机器上创建一个进程,提供调度服务.使用了 APScheduler. 安装:APScheduler $ wget https://pypi ...
- Java四大名著下载大全(中文+英文)
转自:http://www.blogjava.net/kuuyee/archive/2013/06/03/400084.html 抽时间整理了一下Java四大名著,分享出来方便大家学习! Note 郑 ...