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, ...
随机推荐
- AR模块常用函数
--AR模块常用函数 FUNCTION get_fnd_user_name ( p_user_id IN NUMBER ) return VARCHAR2 IS CURSOR c_user_name ...
- 使用Apache的ab进行压力测试
概述 ab是apache自带的压力测试工具,当安装完apache的时候,就可以在bin下面找到ab然后进行apache 负载压力测试. 后台测试开发中,常用的压力测试服务,php一般选择xampp,下 ...
- Microsoft Dynamics CRM 2011 当您在 大型数据集上执行 RetrieveMultiple 查询很慢的解决方法
症状 当您在 Microsoft Dynamics CRM 2011 年大型数据集上执行 RetrieveMultiple 查询时,您会比较慢. 原因 发生此问题是因为大型数据集缓存 Retrieve ...
- 【shell点滴】参数变量
参数变量故名思议就是用来操作输入参数的变量,知道用户输入了哪些参数,才可以进行相应的处理. 参数变量 作用 $1,$2- 取第几个参数的意思 $* 取出所有的参数,解析参数的分割符环境变量 IFS 来 ...
- 最简单的基于DirectShow的示例:视频播放器图形界面版
===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视 ...
- 【shell脚本】mysql每日备份shell脚本
每天固定时间用mysqldump 备份mysql数据. #!/bin/bash #每天早上4点, mysql备份数据 orangleliu #chmod 700 backup.sh #crontab ...
- Hessian源码分析--总体架构
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...
- ubuntu make menuconfig error
主机环境:ubuntu -------------------------------------------------------------- 在ubuntu系统中,要编译内核,还需要安装一系列 ...
- 03 ProgressBar 进度条
> style="?android:attr/progressBarStyleSmall" 样式 android:progress= ...
- Tom DeMarco:软件工程这个概念已过时?
原文作者:Tom Demarco,写于2009年7月 作者简介:Tom DeMarco是大西洋系统协会(www.atlsysguild.com)的负责人.他的职业生涯开始于贝尔实验室,是结构化分析和设 ...