工作项目中不少用到动画的地方,总结一下。
方案1:frame 实现动画
思路:更改frame 的 size 和 origin 既可以达到简单的想要的效果
代码:
- (void)viewDidLoad {
[super viewDidLoad];
// 简单位移和缩放动画
UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(50, 100, 80, 80)];
button.backgroundColor = [UIColor redColor];
[self.view addSubview:button];
[button addTarget:self action:@selector(move) forControlEvents:UIControlEventTouchUpInside];
self.button = button;
}
- (void)move
{
// 2.0s之内完成动画
[UIView animateWithDuration:2.0 animations:^{
CGRect frame = CGRectMake(100, 200, 120, 120);
self.button.frame = frame;
self.button.backgroundColor = [UIColor greenColor];
}];
}
动画效果:
方案2:transform 实现动画
1> 创建一个transform属性
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) ;
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
2> 在transform的基础上进行叠加
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);
3> 清空之前设置的transform属性
view.transform = CGAffineTransformIdentity;
代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(100, 50, 80, 80)];
button.backgroundColor = [UIColor redColor];
[self.view addSubview:button];
self.button = button;
[button addTarget:self action:@selector(move) forControlEvents:UIControlEventTouchUpInside];
}
- (void)move{
[UIView animateWithDuration:2.0 animations:^{
// 位置移动
[self.button setTransform:CGAffineTransformMakeTranslation(100, 300)];
// 缩放,在上一次 transform 的基础上进行的操作
[self.button setTransform:CGAffineTransformScale(self.button.transform, 1.5, 1.5)];
// 旋转,也是在上一次的基础上进行的操作
[self.button setTransform:CGAffineTransformRotate(self.button.transform, 2.0f)];
} completion:^(BOOL finished){
// 重新回到原始的状态
self.button.transform = CGAffineTransformIdentity;
}];
}
动画效果
方案3:帧动画
- 帧动画相关属性和方法
1> 属性
// 序列帧图片数组
@property(nonatomic,copy) NSArray *animationImages;
// 帧动画持续的时间
@property(nonatomic)NSTimeInterval animationDuration;
// 帧动画执行的次数
@property(nonatomic)NSInteger animationRepeatCount;
2> 方法
- (void)startAnimating;
- (void)stopAnimating;
- (BOOL)isAnimating;
代码:
@interface ViewController ()
@property (strong,nonatomic) UIImageView * imageView;
@property (strong,nonatomic) NSMutableArray * array;
@end
@implementation ViewController
- (NSMutableArray *)array
{
if (_array == nil) {
_array = [NSMutableArray array];
for (int i = 0; i < 26; i++) {
NSString * path = [NSBundle mainBundle].bundlePath;
// 引出一个问题 png 和 JPG 格式图片加载的区别
path = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"angry_%02d.jpg",i]];
// imageWithContentOfFile:和 imageWithName:的区别
UIImage * image = [UIImage imageWithContentsOfFile:path];
[_array addObject:image];
}
}
return _array;
}
- (void)viewDidLoad {
[super viewDidLoad];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
imageView.image = [UIImage imageNamed:@"angry_00.jpg"];
self.imageView = imageView;
[self.view addSubview:self.imageView];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (self.imageView.isAnimating) return;
NSTimeInterval time = self.array.count * 0.075;
self.imageView.animationImages = self.array;
self.imageView.animationDuration = time;
self.imageView.animationRepeatCount = 1;
[self.imageView startAnimating];
[self.imageView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:time];
}
@end
帧动画的代码中引出两个问题:
png 和 jpg 的区别
iOS 开发中 png 和 jpg图片的区别imageWithName:和 imageWithContentOfFile:的区别
imageWithName:
有缓存(程序所占的内存会一直停留在程序中)
imageWithContentOfFile:
无缓存(图片所占内存会在一些特定的操作后被清除)
方案4:过渡动画
api:
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
代码:
@interface ViewController ()
@property (strong,nonatomic) UIView * someView;
@property (strong,nonatomic) UIView * view1;
@property (strong,nonatomic) UIView * view2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.someView = [[UIView alloc] initWithFrame:CGRectMake(20, 40, 100, 100)];
self.someView.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.someView];
_view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
_view1.backgroundColor = [UIColor redColor];
_view1.alpha = 0;
[self.someView addSubview:_view1];
_view2 = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 50)];
_view2.backgroundColor = [UIColor blueColor];
[self.someView addSubview:_view2];
}
- (void)viewDidAppear:(BOOL)animated
{
[UIView transitionWithView:self.someView duration:2.5 options:
UIViewAnimationOptionCurveEaseInOut animations:^{
_view2.alpha = 0;
self.someView.center = CGPointMake(200, 400);
_view1.alpha = 1;
} completion:^(BOOL finished) {
}];
}
方案5:隐式动画
隐式动画是针对CALayer来说的
每个
UIView
的内部都默认关联着一个CALayer
层,我们称这个 Layer 为root layer
。所有的非root layer
层,也就是手动创建的CALayer
对象,都存在者隐式动画。
隐式动画:对非root layer
的部分属性进行修改时,默认会自动产生一些动画效果,这些属性称为Animatable Properties
(可动画属性)
bounds
: 产生缩放动画
backgroundColor
: 背景色的渐变
position
:产生平移效果
可以通过动画事务关闭默认的隐式动画效果
代码:
@interface ViewController ()
@property (strong,nonatomic) CALayer * layer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CALayer * layer = [CALayer layer];
layer.frame = CGRectMake(50, 50, 80, 80);
layer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:layer];
self.layer = layer;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 开启事务
[CATransaction begin];
// 设置可以进行动画
[CATransaction setDisableActions:YES];
// 设置 calayer 背景色
self.layer.backgroundColor = [UIColor greenColor].CGColor;
// 更改尺寸
self.layer.frame = CGRectMake(200, 200, 100, 100);
self.layer.opacity = 0.5;
[CATransaction commit];
}
@end
效果:
方案6:核心动画 CoreAnimation
开发步骤:
- 初始化一个动画并设置一些动画属性
- 添加动画对象到层中
- 开始执行动画
Core Animation 的动画执行过程都是在后台操作的,不会阻塞主线程
CABasicAnimation
1 > keyPath 动画类型
1.平移动画: position/transform.translation.x
2.旋转动画 : transform.rotation/transform.rotation.x /transform.rotation/rotation.y
3.缩放动画:bounds/transform.scale/transform.scale.x/transform.scale.y
2> fromValue 动画开始位置
3> toValue 动画结束位置
4> byValue 动画增加值
5> removedOnCompletion 动画完成后不要删除
6> fillMode 保持最新状态
代码:
#import "ViewController.h"
// later equals never
typedef NS_ENUM(NSUInteger,AnimationType){
kAnimationScale,
kAnimationRotate,
kAnimationTranslate
};
@interface ViewController ()
@property (strong,nonatomic) CALayer * layer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CALayer * layer = [CALayer layer];
layer.frame = CGRectMake(50, 50, 80, 80);
layer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:layer];
self.layer = layer;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self testAnimation:kAnimationRotate];
[self testAnimation:kAnimationTranslate];
// [self testAnimation:kAnimationScale];
}
- (void)testAnimation:(AnimationType) type
{
// 1.创建动画对象
CABasicAnimation * animation = [CABasicAnimation animation];
// 2.设置动画属性
switch (type) {
case kAnimationScale:
// keyPath 决定执行怎样的动画,调整那个属性来执行动画,
// 缩放动画
animation.keyPath = @"bounds";
animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 150, 150)];
break;
case kAnimationRotate:
// 旋转动画
animation.keyPath = @"transform";
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 1, 0)];
break;
case kAnimationTranslate:
// 平移动画
animation.keyPath = @"position";
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 400)];
default:
break;
}
animation.duration = 2.f;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
// 3.添加动画
[self.layer addAnimation:animation forKey:nil];
}
@end
动画效果:
CAKeyframeAnimation
新增几个属性
path:运动的路线
values: 关键帧(如果设置了 path,values 是被忽略的)
keyTimes:为对应的关键帧指定的时间点,范围是0~1的
代码:
#import "ViewController.h"
@interface ViewController ()
@property (strong,nonatomic) UIView * redView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView * redView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 80, 80)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
self.redView = redView;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// [self test1];
[self test2];
}
// 设置定点的运动
- (void)test2
{
// 1.创建动画对象
CAKeyframeAnimation * anim = [CAKeyframeAnimation animation];
// 2.设置动画
anim.keyPath = @"position";
NSValue *v1 = [NSValue valueWithCGPoint:CGPointZero];
NSValue *v2 = [NSValue valueWithCGPoint:CGPointMake(100, 0)];
NSValue *v3 = [NSValue valueWithCGPoint:CGPointMake(100, 200)];
NSValue *v4 = [NSValue valueWithCGPoint:CGPointMake(0, 200)];
anim.values = @[v1,v2,v3,v4];
anim.duration = 2.0f;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
// 3.添加动画
[self.redView.layer addAnimation:anim forKey:nil];
}
// 按照所画的轨迹进行运动
-(void)test1
{
// 1.创建动画对象
CAKeyframeAnimation * anim = [CAKeyframeAnimation animation];
// 2.设置动画属性
anim.keyPath = @"position";
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(100, 100, 200, 200));
anim.path = path;
CGPathRelease(path);
// 动画的执行节奏
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
anim.duration = 2.0f;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
anim.delegate = self;
[self.redView.layer addAnimation:anim forKey:nil];
}
#pragma mark - 监听动画的执行过程
- (void)animationDidStart:(CAAnimation *)anim
{
NSLog(@"动画开始");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(@"动画停止");
}
@end
动画效果(value 动画 path 动画):
CAAnimationGroup 动画组
新增属性
animations
代码:
#import "ViewController.h"
@interface ViewController ()
@property (strong,nonatomic) UIView * redview;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIView * redview = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 80, 80)];
redview.backgroundColor = [UIColor redColor];
[self.view addSubview:redview];
self.redview = redview;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 1.创建旋转动画对象
CABasicAnimation * rotate = [CABasicAnimation animation];
rotate.keyPath = @"transform.rotation";
rotate.toValue = @(M_PI);
// 2.创建缩放动画
CABasicAnimation * scale = [CABasicAnimation animation];
scale.keyPath = @"transform.scale";
scale.toValue = @(0.0);
// 3.平移动画
CABasicAnimation * move = [CABasicAnimation animation];
move.keyPath = @"position";
move.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 200)];
// 4.将动画放到动画组
CAAnimationGroup * group = [CAAnimationGroup animation];
group.animations = @[rotate,scale,move];
group.duration = 2.0f;
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
[self.redview.layer addAnimation:group forKey:nil];
}
@end
动画效果:
转场动画 CATransition
UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果
属性解析:
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点(在整体动画的百分比)
endProgress:动画终点(在整体动画的百分比)
代码:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (nonatomic, assign) int index;
@end
@implementation ViewController
- (IBAction)prebtn:(id)sender {
self.index--;
if (self.index == -1) {
self.index = 8;
}
NSString * filename = [NSString stringWithFormat:@"%d.jpg", self.index + 1];
self.imageView.image = [UIImage imageNamed:filename];
CATransition * anim = [CATransition animation];
anim.type= @"cube";
anim.subtype = kCATransitionFromLeft;
anim.duration = 0.5;
[self.view.layer addAnimation:anim forKey:nil];
}
- (IBAction)nextbtn:(id)sender {
self.index++;
if (self.index == 9) {
self.index = 0;
}
NSString * filename = [NSString stringWithFormat:@"%d.jpg",self.index+1];
self.imageView.image = [UIImage imageNamed:filename];
CATransition * anim = [CATransition animation];
// 设置过渡类型
anim.type = @"cube";
// 设置过渡方向
anim.subtype = kCATransitionFromRight;
anim.duration = 0.5;
[self.view.layer addAnimation:anim forKey:nil];
}
Github 地址:https://github.com/fuandenghuo/AnimationDemes
--于北京昌平