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, ...
随机推荐
- EBS开发之环境迁移
(一)环境迁移说明 1.1 迁移 由于EBS系统开发复杂,一般项目实施都是使用三套或者三套以上的系统,一套作为开发使用系统,一套作为集成测试系统,一套就是企业用的正式环境系统,在项目实施过程中对一 ...
- webstorm工具使用详解
webstorm简单介绍 官网地址:http://www.jetbrains.com/webstorm/features/index.html 参考地址:http://www.html5jscss.c ...
- 10 GridView 样式属性
GridView 样式属性: 1,android:numColumns="auto_fit" 显示的列数 如果android:numColumns不设置那么自动每行1列 如下图 2 ...
- NewSQL数据库VoltDB特性简介
VoltDB是一个革命性的新型数据库产品,被称作NewSQL数据库.它基于H-Store,号称比当前数据库产品的吞吐量高45倍,同时又具有很高的扩展性.它的特性主要有以下几点: Ø 高吞吐.低延迟: ...
- DFS(深度优先)算法编程实践
DFS定义 DFS(Depth-First-Search)深度优先搜索算法,是搜索算法的一种.是一种在开发爬虫早期使用较多的方法.它的目的是要达到被搜索结构的叶结点 . 特点 每次深度优先搜索的结果必 ...
- [django]用日期来查询datetime类型字段
有一个model的字段是 DateTimeField,我现在要具体查询某一天date的数据,应该怎么用orm来查询呢? 指定年月日 YourModel.objects.filter(datetime_ ...
- Java并发框架——公平性
所谓公平性指所有线程对临界资源申请访问权限的成功率都一样,不会让某些线程拥有优先权.通过前面的CLH Node FIFO学习知道了等待队列是一个先进先出的队列,那么是否就可以说每条线程获取锁时就是公平 ...
- Cocos2D iOS之旅:如何写一个敲地鼠游戏(十一):完善游戏逻辑
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- DB2数据库常用命令
--创建数据库 CREATE DATABASE example AUTOMATIC STORAGE YES --自动存储 ON 'D:\' DBPATH ON'D:\' --指定数据库控制文件的存储路 ...
- x264 n-th pass编码时候Stats文件的含义
x264 n-th pass(一般是2pass)编码时所用的文件包括下述x264参数生成.stats文件 options: 1280x816 fps=2997/125 timebase=125/299 ...