实现效果

刚拿到设计稿的时候大概看了一眼,当时心里想着放张背景图,然后计算下相应点的坐标,在最上面画一层就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实现的更多相关文章

  1. <Android 应用 之路> MPAndroidChart~BubbleChart(气泡图) and RadarChart(雷达图)

    简介 MPAndroidChart是PhilJay大神给Android开发者带来的福利.MPAndroidChart是一个功能强大并且使用灵活的图表开源库,支持Android和iOS两种,这里我们暂时 ...

  2. iOS启动图和开屏广告图,类似网易

    iOS启动图和开屏广告图,类似网易 启动图是在iOS开发过程中必不可少的一个部分,很多app在启动图之后会有一张自定义的开屏广告图,点击该广告图可以跳转到广告图对应的页面.今天呢,和大家分享一下如何添 ...

  3. iOS多图上传

    iOS多图上传涉及到多线程问题,个人比较喜欢使用GCD操作,下边是最近写的一个多图上传代码,附带相关注释 __block BOOL allSucc = YES; __block int m = 0; ...

  4. iOS 1 到 iOS 10 ,我都快老了

    iOS 1:iPhone诞生 虽然很难想像,但初代iPhone在问世时在功能方面其实远远落后于那时的竞争对手,比如Windows Mobile.Palm OS.塞班.甚至是黑莓.它不支持3G.多任务. ...

  5. IOS学习之IOS沙盒(sandbox)机制和文件操作

    IOS学习之IOS沙盒(sandbox)机制和文件操作(一) 1.IOS沙盒机制 IOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都 ...

  6. 利用d3.js绘制雷达图

    利用d3,js将数据可视化,能够做到数据与代码的分离.方便以后改动数据. 这次利用d3.js绘制了一个五维的雷达图.即将多个对象的五种属性在一张图上对照. 数据写入data.csv.数据类型写入typ ...

  7. IOS 动画专题 --iOS核心动画

    iOS开发系列--让你的应用“动”起来 --iOS核心动画 概览 通过核心动画创建基础动画.关键帧动画.动画组.转场动画,如何通过UIView的装饰方法对这些动画操作进行简化等.在今天的文章里您可以看 ...

  8. 李洪强iOS开发之iOS好文章收集

    李洪强iOS开发之iOS好文章收集 该文收集朋友们转发或自己的写的技术文章,如果你也有相关的好文章,欢迎留言,当好文章多的时候,我会对这些好文章进行分门别类 文章 简述 日期 直播服务配置 使用 ng ...

  9. R语言绘图:雷达图

    使用fmsb包绘制雷达图 library("fmsb") radarfig <- rbind(rep(90, 4), rep(60, 4), c(86.17, 73.96, ...

随机推荐

  1. 十六进制字符串转化为十进制值strtoul函数

    eg: NSString *strtest =@"7fffffff"; NSUInteger val = strtoul([[strtest substringWithRange: ...

  2. Android的log日志知识点剖析

    log类的继承结构 Log public final class Log extends Object java.lang.Object ↳ android.util.Log log日志的常用方法 分 ...

  3. Android Multimedia框架总结(六)C++中MediaPlayer的C/S架构

    转载请把头部出处链接和尾部二维码一起转载,本文出自: http://blog.csdn.net/hejjunlin/article/details/52435789 前面几节中,都是通过java层调用 ...

  4. Freemarker 浅析

    今天分享一下一个模板语言的使用,它就是Freemarker,有点类似与前些日子做Python的Django中的模板语言,其实原理上都是相似的.所以这里就不对那些基础性的语法类的直至进行讲解了,就拿几个 ...

  5. Dynamics CRM 插件Plugin中获取和更新时间字段值的准确转换

    前面两篇介绍了后台代码通过组织服务获取更新时间字段.窗体javascript通过Odata获取更新时间字段,最后篇来实验下在插件中的获取和更新时间字段是否需要时制的转化,为何说是最后篇呢,因为在CRM ...

  6. Android初级教程通过简要分析“土司”源码,来自实现定义土司理论探讨

    由于系统自带的土司瞬间即逝,而且非常难看.因此我们就希望自定义自己的土司风格.有些实例就是基于自定义土司完成的,例如金山卫士的火箭发射,基本原理就是个土司.但是在做出自己的土司风格之前,还是要简要分析 ...

  7. 【一天一道LeetCode】#191. Number of 1 Bits

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...

  8. 【Android 应用开发】 Android 相关代码规范 更新中 ...

    . 简介 : Android 常用的代码结构, 包括包的规范, 测试用例规范, 数据库模块常用编写规范; 参考 : 之前写的一篇博客 [Android 应用开发] Application 使用分析 ; ...

  9. 一个Bootstrap的例子--关于validate

    </pre><pre name="code" class="html"><%@ page language="java& ...

  10. Chapter 3 Protecting the Data(4):创建和使用应用程序角色

    原文出处:http://blog.csdn.net/dba_huangzj/article/details/39927713,专题目录:http://blog.csdn.net/dba_huangzj ...