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