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, ...
随机推荐
- Codeforces Round #266 (Div. 2) D
D. Increase Sequence time limit per test 1 second memory limit per test 256 megabytes input standard ...
- calabash-android Win10 入门笔记
参考官方文档:https://developer.xamarin.com/guides/testcloud/calabash/ 概述 Calabash是一个BDD的UI自动化验收测试框架, ...
- Sampling
本文主要涉及接受拒绝采样,重要性采样,蒙特卡洛方法,吉布斯采样等内容.部分内容整理与互联网.仅供交流学习使用!
- multiprocssing,threading,多进程多线程初识
1. multiprocessing 多进程: import multiprocessing p = multiprocessing.Process(target = func,name = &quo ...
- Pandas简易入门(一)
目录: 读取数据 索引 选择数据 简单运算 声明,本文引用于:https://www.dataquest.io/mission/8/introduction-to-pandas (建议阅读原文) Pa ...
- Spring 学习03
一.上节内容回顾 1 注解ioc操作 (1)使用注解创建对象 - 四个注解 (2)使用注解注入属性 - 两个注解 2 aop (1)aop原理 (2)aop术语 - 切入点 - 增强 - 切面 3 s ...
- ODAC(V9.5.15) 学习笔记(九)TOraSQLMonitor
名称 类型 说明 Active Boolean 激活SQL跟踪 DBMonitorOptions 将跟踪信息发送到dbMonitor工具软件的选择项 Host IP地址 Port 端口号 Reconn ...
- 【CF1132F】Clear the String (DP)
/* 区间dp题目, 考虑当前区间l,r 是可以枚举最后一次拿的分界点来考虑最右边节点是不是具有贡献 */ #include<cstdio> #include<algorithm&g ...
- Python中的 // 与 / 的区别
" / " 表示浮点数除法,返回浮点结果;" // " 表示整数除法,返回不大于结果的一个最大的整数 [code] print("6 // 4 = & ...
- 最好用的xshell替代软件----FinalShell工具
2017年8月份NetSarang公司旗下软件家族的官方版本被爆被植入后门着实让我们常用的Xshell,Xftp等工具火了一把,很长时间都是在用Xshell,不过最近发现了一款同类产品FinalShe ...