UIView圆形动画

自己封装的一个动画效果=。=

UIView圆圈显示隐藏动画

在一些日常的开发中,常用的动画有:

  • UIView Animation配合UIViewframealphatransform等属性变化,能几行代码实现渐入、渐出、缩放、位移、旋转等常用动画效果。
  • Spring能一句代码实现弹簧动画。
  • animationKeyframes可以实现颜色渐变动画。
  • transition动画可以实现翻转动画。

对这些动画组合使用能满足很多日常的需求。
简单实用但不够炫酷。
今天先介绍一个简单的实例,效果如下:
HZAnimation

实现思路

  • 首先获取并记录动画对象Viewframe和其子视图的frame
  • 计算出以动画对象View的斜对角线作为直径,中心为圆点。
  • 使用UIBezierPath初始状态最终状态画出来。(这里显示的初始状态是圆点,最终状态是斜对角线为直径,中点为圆心的圆。隐藏时的以此类推)。
  • 创建一个关于PathCABasicAnimation动画从初始状态最终状态
  • 将子视图的frame设置为父视图的中心。
  • 为动画对象的.layer.mask属性添加一个layer,并添加CABasicAnimation动画,同时用UIView animation实现子视图复原动画。

显示动画完成,隐藏以此类推咯。


代码实现

创建一个UIViewCategoryHZAnimation

UIView+HZAnimation.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIView (HZAnimation)<CAAnimationDelegate>
typedef void (^HZAnimationBlock)(void);

/**
圆形遮罩显示动画
*/
- (void)HZCircleShowAnimationDuration:(NSTimeInterval)animationTime Completed:(HZAnimationBlock)completed;
/**
圆形遮罩隐藏动画
*/
- (void)HZCircleHideAnimationDuration:(NSTimeInterval)animationTime Completed:(HZAnimationBlock)completed;

@end

NS_ASSUME_NONNULL_END

UIView+HZAnimation.m

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//
// UIView+HZAnimation.m
// HZViewAnimationDemo
//
// Created by 王浩祯 on 2019/4/9.
// Copyright © 2019 Hades. All rights reserved.
//

#import "UIView+HZAnimation.h"
@implementation UIView (HZAnimation)

- (void)HZCircleShowAnimationDuration:(NSTimeInterval)animationTime Completed:(HZAnimationBlock)completed{

[self HZAnimationShowDuration:animationTime CustomColor:self.backgroundColor SubviewsAnimation:YES Completed:^{
if (completed) {
completed();
}
}];

}

- (void)HZCircleHideAnimationDuration:(NSTimeInterval)animationTime Completed:(HZAnimationBlock)completed{

[self HZAnimationHideDuration:animationTime CustomColor:self.backgroundColor SubviewsAnimation:YES Completed:^{
if (completed) {
completed();
}
}];

}

#pragma mark -- HZAnimation detail ------ =͟͟͞͞⊂(⊂ 'ω')
- (void)HZAnimationShowDuration:(NSTimeInterval)animationTime CustomColor:(UIColor *)color SubviewsAnimation:(BOOL)isSubviewsAnimation Completed:(HZAnimationBlock)completed{

//记录父视图的属性
UIColor* originalColor = self.backgroundColor;
CGFloat originalWid = self.frame.size.width;
CGFloat originalHei = self.frame.size.height;
CGFloat animationViewLength;

NSMutableArray* subviewsFrameArr = [NSMutableArray new];

//计算动画视图圆的半径
animationViewLength = sqrt(originalWid * originalWid + originalHei * originalHei)/2 ;

//绘制路径
UIBezierPath* originalCircle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(originalWid/2, originalHei/2, 0, 0)];

UIBezierPath* finalCircle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(originalWid/2 - animationViewLength, originalHei/2 - animationViewLength, animationViewLength * 2, animationViewLength * 2)];

//创建一个关于Path的CABasicAnimation动画从originalCircle到finalCircle
CABasicAnimation *hzAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
hzAnimation.fromValue = (__bridge id)(originalCircle.CGPath);
hzAnimation.toValue = (__bridge id)((finalCircle.CGPath));
hzAnimation.duration = animationTime;
hzAnimation.delegate = self;
//填充效果:动画结束后,动画将保持最后的表现状态
hzAnimation.fillMode = kCAFillModeForwards;
hzAnimation.removedOnCompletion = NO;
//记录子视图frame并修改
if (isSubviewsAnimation) {

for (UIView* viewObj in self.subviews) {

[subviewsFrameArr addObject:[NSValue valueWithCGRect:viewObj.frame]];
viewObj.frame = CGRectMake(originalWid/2, originalHei/2, 0, 0);

}
}
//给mask属性赋值一个layer
CAShapeLayer *maskLayer = [CAShapeLayer layer];
self.layer.mask = maskLayer;
self.layer.masksToBounds = YES;
[maskLayer addAnimation:hzAnimation forKey:@"path"];
[UIView animateWithDuration:animationTime animations:^{

if (isSubviewsAnimation) {
NSInteger subviewsNumber = 0;
for (int i = 0; i < self.subviews.count; i ++) {
UIView* viewObj = self.subviews[i];

viewObj.frame = [subviewsFrameArr[subviewsNumber] CGRectValue];
subviewsNumber ++;

}
}


} completion:^(BOOL finished) {
self.backgroundColor = originalColor;
completed();
}];

}

- (void)HZAnimationHideDuration:(NSTimeInterval)animationTime CustomColor:(UIColor *)color SubviewsAnimation:(BOOL)isSubviewsAnimation Completed:(HZAnimationBlock)completed{

//记录父视图的属性
CGFloat originalWid = self.frame.size.width;
CGFloat originalHei = self.frame.size.height;
CGFloat animationViewLength;

//计算动画视图圆的半径
animationViewLength = sqrt(originalWid * originalWid + originalHei * originalHei)/2 ;
//绘制路径
UIBezierPath* originalCircle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(originalWid/2 - animationViewLength, originalHei/2 - animationViewLength, animationViewLength * 2, animationViewLength * 2)];

UIBezierPath* finalCircle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(originalWid/2, originalHei/2, 0, 0)];

CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = finalCircle.CGPath;

self.layer.mask = maskLayer;

//创建一个关于Path的CABasicAnimation动画从originalCircle到finalCircle
CABasicAnimation *hzAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
hzAnimation.fromValue = (__bridge id)(originalCircle.CGPath);
hzAnimation.toValue = (__bridge id)((finalCircle.CGPath));
hzAnimation.duration = animationTime;
hzAnimation.delegate = self;


self.layer.masksToBounds = YES;
[maskLayer addAnimation:hzAnimation forKey:@"path"];
[UIView animateWithDuration:animationTime animations:^{

if (isSubviewsAnimation) {

for (UIView* viewObj in self.subviews) {
viewObj.frame = CGRectMake(originalWid/2, originalHei/2, 0, 0);
}

}

} completion:^(BOOL finished) {

completed();
}];


}

#pragma mark - CABasicAnimation的Delegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"caani end");
//清除 fromVC 的 mask
self.layer.mask = nil;

}

@end

具体使用

显示

1
2
3
[<#your UIView obj#> HZAnimationShowDuration:<#AnimationDuration#>  Completed:^{
NSLog(@"Animation completed");
}];

隐藏

1
2
3
4
5
[<#your UIView obj#> HZAnimationHideDuration:<#AnimationDuration#> Completed:^{
//TODO: remove view from superView
//other thing
NSLog(@"Animation completed");
}];

项目DEMO

HZAniamtion
给个star吧!!铁汁!