iOS核心动画笔记

  1. "小画板程序"
    完成"小画板"程序。

下载地址:
http://git.oschina.net/changyou/mySmallPainter/repository/archive/master

git链接 :
https://git.oschina.net/changyou/mySmallPainter.git

  1. CALayer介绍
    2.1. CALayer
    2.1 CALayer的常见属性
  • 设置控制器 view 的背景颜色, 更好的观察
    self.view.backgroundColor = [UIColor grayColor];

  • 如何让"控件"变的更好看? 通过操作控件的 CALayer, 修改控件的外观。

  • "阴影效果"
    参考:

// "阴影"效果(shadowColor、shadowOffset、shadowOpacity属性需要同时设置后才可以看到)
// 设置"阴影"的颜色, 注意 UIKit 框架中的颜色不能直接设置给 CGColorRef
self.demoView.layer.shadowColor = [UIColor yellowColor].CGColor;
// 设置"阴影"的偏移
self.demoView.layer.shadowOffset = CGSizeMake(5, 5);
// 设置"阴影"的透明度(layer 的 opacity 相当于 view 的 alpha)
self.demoView.layer.shadowOpacity = 1.0;
// 设置"阴影"半径
self.demoView.layer.shadowRadius = 10;
  • "圆角效果"
  • 画图解释"圆角半径"的含义
  • 注意: 对于正方形来说, 当圆角半径为边长的一半的时候, 就是一个圆形
  • 可以用这种方式对控件截图
    参考:
// 设置"圆角"效果
self.demoView.layer.cornerRadius = 20;
  • 设置"边框效果"
    参考:
// 设置"边框"效果
self.demoView.layer.borderColor = [UIColor whiteColor].CGColor;
self.demoView.layer.borderWidth = 2;
  • 通过 UIImageView 实现"图片裁剪"
    参考:
- (void)viewDidLoad {
    [super viewDidLoad];
    // 设置控制器 view 的背景颜色, 更好的观察
    self.view.backgroundColor = [UIColor grayColor];
    [self demoImageView];
     // 设置"阴影"
    //    self.demoImageView.layer.shadowColor = [UIColor yellowColor].CGColor;
    //    self.demoImageView.layer.shadowRadius = 70;
    //    self.demoImageView.layer.shadowOffset = CGSizeMake(0, 0);
    //    self.demoImageView.layer.shadowOpacity = 1.0;
    self.demoImageView.layer.cornerRadius = 50;
    self.demoImageView.layer.borderWidth = 3;
    self.demoImageView.layer.borderColor = [UIColor blueColor].CGColor;
    // 注意: "阴影"效果" 和 "裁剪"不能同时应用
    // CALayer 的 masksToBounds 类似于 UIView 的 clipsToBounds
    self.demoImageView.layer.masksToBounds = YES;
    // 保存图片
 UIGraphicsBeginImageContextWithOptions(self.demoImageView.bounds.size, NO, 0.0);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [self.demoImageView.layer renderInContext:ctx];
    UIImage *imgIcon = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageWriteToSavedPhotosAlbum(imgIcon, nil, nil, nil);
}
  • 2.2 "新建 CALayer", 在 CALayer 中嵌套 CALayer(CALayer就像 UIView 一样, 也可以实现嵌套)
    1> 在控制器的 self.view 中添加一个普通的 layer
    参考:
 - (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view, typically from a nib.
     CALayer *layer = [[CALayer alloc] init];
     // ------------------- 设置位置大小 ---------------------
     // 方式一: 直接设置 frame
     // layer.frame = CGRectMake(50, 50, 200, 200);
     // 方式二:
     // 先设置大小
     layer.bounds = CGRectMake(0, 0, 100, 100);
     // 再设置位置(默认情况下 position 指的是 center。)
     layer.position = CGPointMake(150, 150);
     // ------------------- 设置位置大小 ---------------------
     layer.backgroundColor = [UIColor redColor].CGColor;
     layer.opacity = 0.7;
     [self.view.layer addSublayer:layer];
 }

2> 在控制器的 view 中添加一个能显示图片的 layer

  • 思路:
  • 向普通的自定义的 layer 中设置内容即可。设置 layer.contents属性。
  • 查看 layer.contents属性的介绍

参考:

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CALayer *layer = [[CALayer alloc] init];
// ------------------- 设置位置大小 ---------------------
// 方式一: 直接设置 frame
// layer.frame = CGRectMake(50, 50, 200, 200);
// 方式二:
// 先设置大小
layer.bounds = CGRectMake(0, 0, 100, 100);
// 再设置位置(默认情况下 position 指的是 center。)
layer.position = CGPointMake(150, 150);
// ------------------- 设置位置大小 ---------------------
layer.backgroundColor = [UIColor redColor].CGColor;
layer.opacity = 1.0;
UIImage *imgBg = [UIImage imageNamed:@"header_home"];
// UIKit 中使用的是 Foundation 框架, 而 Foundation 框架又使用的是 Core Foundation 框架
// Core Foundation 框架都是 C 语言的, 一般凡是 Core Xxxx 的框架都是 C 语言的。
// __bridge 类型 表达式, 的作用一般就是把 Core Foundation 中的数据类型转换成 Foundation 中的类型, 桥接的时候也会设置到一些所有权的转换等。
layer.contents = (__bridge id)(imgBg.CGImage);
[self.view.layer addSublayer:layer];
}

3> CALayer的"可动画属性"

  • 凡是文档中有"Animatable"字样的属性都是可动画属性
  • 可动画属性就是说: 只要设置了属性(改变了属性), 会自动使用画的方式来执行。

// 通过position 修改 位置

  • 案例: 在 touchesBegan: 方法中, 获取当前触摸点的位置, 然后设置 layer的 position为这个触摸点。
  • 效果: 设置完毕 position后, 自动会以动画的方式来执行。(可动画属性)
    参考:
@interface ViewController ()
@property (nonatomic, weak) CALayer *myLayer;
@end
@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CALayer *layer = [[CALayer alloc] init];
self.myLayer = layer;
// ------------------- 设置位置大小 ---------------------
// 方式一: 直接设置 frame
// layer.frame = CGRectMake(50, 50, 200, 200);
// 方式二:
// 先设置大小
layer.bounds = CGRectMake(0, 0, 100, 100);
// 再设置位置(默认情况下 position 指的是 center。)
layer.position = CGPointMake(150, 150);
// ------------------- 设置位置大小 ---------------------
layer.backgroundColor = [UIColor redColor].CGColor;
layer.opacity = 1.0;
UIImage *imgBg = [UIImage imageNamed:@"header_home"];
// UIKit 中使用的是 Foundation 框架, 而 Foundation 框架又使用的是 Core Foundation 框架
// Core Foundation 框架都是 C 语言的, 一般凡是 Core Xxxx 的框架都是 C 语言的。
// __bridge 类型 表达式, 的作用一般就是把 Core Foundation 中的数据类型转换成 Foundation 中的类型, 桥接的时候也会设置到一些所有权的转换等。
layer.contents = (__bridge id)(imgBg.CGImage);
[self.view.layer addSublayer:layer];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:touch.view];
self.myLayer.position = point;
}
@end

总结: iOS 中的动画都是"属性动画", 只要修改 layer的动画属性, 即可实现动画。联想之前的[UIView animateWithDuration:]动画方法。

// 通过 transform 属性进行形变

  • 案例: 通过 CALayer的 transform属性实现"旋转"、"缩放"、"平移"
    1> 缩放:
    参考:
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
     UITouch *touch = touches.anyObject;
     CGPoint point = [touch locationInView:touch.view];
     // 设置 layer 的位置(默认 position 表示中心点的位置)
     self.myLayer.position = point;
      // 缩放(生成一个0.6~ 1.0之间的随机数)
     //    CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5; //0.5;
     //    self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);
     // 旋转
     CGFloat rotate = arc4random_uniform(M_PI * 2);
     self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 5, 0);
 }

2> 旋转

  • 介绍 x, y, z 轴
    参考:
    从 layer 的中心点到 给定的坐标点之间连一条线, 然后以这个线为中心轴, 开始旋转
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
     UITouch *touch = touches.anyObject;
     CGPoint point = [touch locationInView:touch.view];
     // 设置 layer 的位置(默认 position 表示中心点的位置)
     self.myLayer.position = point;
     // 缩放(生成一个0.6~ 1.0之间的随机数)
     CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5; //0.5;
     self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);
     // 旋转
     CGFloat rotate = arc4random_uniform(M_PI * 2);
     self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 0, 10);
 }

// 问题:在 touchesBegan 方法中先后指定了 transform, 这样后指定的 transform 会覆盖前面的 transform, 所以现在的效果是只旋转,不缩放了。
// 解决: 通过 kvc 解决:
参考解决办法:

// 缩放(生成一个0.6~ 1.0之间的随机数)
CGFloat scale = (arc4random_uniform(5) + 1) / 10.0 + 0.5;
//self.myLayer.transform = CATransform3DMakeScale(scale, scale, 0);
[self.myLayer setValue:@(scale) forKeyPath:@"transform.scale"];
// 旋转
CGFloat rotate = arc4random_uniform(M_PI * 2);
//self.myLayer.transform = CATransform3DMakeRotation(rotate, 0, 0, 10);
[self.myLayer setValue:@(rotate) forKeyPath:@"transform.rotation"];
 struct CATransform3D
 {
     CGFloat m11, m12, m13, m14;
     CGFloat m21, m22, m23, m24;
     CGFloat m31, m32, m33, m34;
     CGFloat m41, m42, m43, m44;
 };
 struct CGAffineTransform {
     CGFloat a, b, c, d;
     CGFloat tx, ty;
 };

// 圆角,透明度,边框
参考:

// 圆角
self.myLayer.cornerRadius = 20;
self.myLayer.masksToBounds = YES;
// 透明度
self.myLayer.opacity = 0.5;
// 设置边框
self.myLayer.borderColor = [UIColor redColor].CGColor;
self.myLayer.borderWidth = 5;

Quartz Core框架不需要导入, 已经默认导入了

  • CALayer的 position属性和 anchorPoint属性介绍
  • anchorPoint是一个 CGPoint类型, 每个值都是一个0~1之间的值

  • 动画属性(隐式动画)需要注意点:
  • 每一个UIView内部都默认关联着一个CALayer, 我们可称这个Layer为Root Layer(根层)
  • 所有的非Root Layer, 也就是手动创建的CALayer对象, 都存在着隐式动画。 root layer 是没有隐式动画的
  • 关闭隐式动画
    参考;
 可以通过动画事务(CATransaction)关闭默认的隐式动画效果
 [CATransaction begin];
 [CATransaction setDisableActions:YES];
 self.myview.layer.position = CGPointMake(10, 10);
 [CATransaction commit];
 - (void)viewDidLoad {
 [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
 CALayer *layer = [[CALayer alloc] init];
 layer.backgroundColor = [UIColor blueColor].CGColor;
 layer.bounds = CGRectMake(0, 0, 100, 100);
 layer.position = CGPointMake(200, 200);
 [self.view.layer addSublayer:layer];
 self.blueLayer = layer;
 }
 
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
 UITouch *touch = touches.anyObject;
 CGPoint point = [touch locationInView:touch.view];
 [CATransaction begin];
 [CATransaction setDisableActions:YES];
 self.blueLayer.position = point;
 [CATransaction commit];
 }

附:


transform的属性列表.png

Snip.png
  • 案例练习:(时钟动画)
  • 使用 CALayer的"定位点(锚点)"实现一个时钟动画(通过 UIView 来实现,目的是为了说明控件的 center 其实就是 CALayer的 position, 通过修改了 position其实就修改了 center 的含义了)
  • 注意: 在使用 Quartz2D 绘图的时候, 对绘图上下文的旋转是对坐标系的旋转, 通过 UI 控件的 transform 对控件做旋转是按照 Center 来旋转的。

  • 注意: 控件的 center 属性, 其实就是对应的 CALayer的 postion。所以控件的 center并不是永远表示控件的中心点。

  • 解决不准确的问题:
    • 通过 NSTimer 实现的动画可能造成卡顿、不连贯的情况(NSTimer 不准确)
    • CADisplayLink 表示"显示连接", 与显示器的刷新频率相关。
    • 显示器每秒钟刷新60次(60HZ)(电视新闻上拍的电脑显示器就不清楚,原因刷新频率不一样)
    • 通过 CADisplayLink 来解决
    参考代码:

 // 核心代码
 CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(moveSecond)];
 [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

 - (void)viewDidLoad {
 [super viewDidLoad];
 // 1. 创建表盘
 UIView *clockView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 150, 150)];
 clockView.backgroundColor = [UIColor blueColor];
 clockView.center = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
 [self.view addSubview:clockView];
 self.clockView = clockView;
 
 // 2. 创建秒针
 UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 5, 50)];
 secondView.backgroundColor = [UIColor redColor];
 secondView.layer.anchorPoint = CGPointMake(0.5, 1);
 secondView.center = clockView.center;
 [self.view addSubview:secondView];
 self.secondView = secondView;
 [self moveSecond];
 
 // 3. 启动一个计时器, 每隔一秒钟计算一下当前秒针的位置, 然后做一次哦旋转操作
 // [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(moveSecond) userInfo:nil repeats:YES];
 
 CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(moveSecond)];
 [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
 }
 
 - (void)moveSecond
 {
 // 1. 计算当前的秒数
 NSDate *nowDate = [NSDate date];
 NSCalendar *calendar = [NSCalendar currentCalendar];
 NSInteger seconds = [calendar component:NSCalendarUnitSecond fromDate:nowDate];
 // 2. 计算每一秒的弧度
 CGFloat rotation = M_PI * 2 / 60;
 // 3. 用每一秒的弧度 * 秒数 , 计算出本次要旋转的弧度
 rotation = seconds * rotation;
 // 4. 旋转秒针
 self.secondView.transform = CGAffineTransformMakeRotation(rotation);
 }
 
 // 每次触摸一次屏幕秒针转一次
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
 CGFloat rotation = M_PI * 2 / 60;
 self.secondView.transform = CGAffineTransformRotate(self.secondView.transform, rotation);
 }
 
 @end
  1. 核心动画
  • 属性动画
    1> 基本动画(只有两帧的动画)
    2> 关键帧动画

  • 组动画

  • 转场动画(场景转换)

2.1 基本动画: 位移
案例: 触摸屏幕, 设置指定的 view 的 layer的 position值
参考:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1. 创建动画对象
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
// 默认动画时间是0.25秒
animation.duration = 2.0;
// 2. 设置动画属性值
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];

// 3. 将动画添加到对应的layer 中
[self.blueView.layer addAnimation:animation forKey:@"animation1"];
}


- (void)viewDidLoad {
[super viewDidLoad];

UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(50, 150, 100, 100)];
blueView.backgroundColor = [UIColor blueColor];
[self.view addSubview:blueView];
self.blueView = blueView;
}
  • 问题:
    1> 动画太快, 默认时间是0.25秒, 通过 duration 属性修改
    2> 动画执行完毕以后回到了起始的位置。
    • 原因:
    • 核心动画的本质是在后台移动图层中的内容, 控件本身的 frame 没有发生改变。
    • 所以看到动画执行完毕后, 又回到了原来的位置
    • 通过设置动画代理来观察动画执行完毕后控件的 frame 值, layer 的 Frame 值, layer 的 position 值, 都是没有变化的

• 解决:
解决1:当动画执行完毕以后, 手动设置控件的位置。在动画的代理方法(动画结束的时候设置控件的 center)
//self.blueView.center = CGPointMake(300, 50);
注意: 不指定 fromValue 的情况下, 如果直接在添加完毕动画后, 设置控件的 center = 最终的终点有问题!!!所以不要在添加完动画以后直接设置 center 为最终的终点, 而要放到代理方法中。

解决2:
动画执行完毕后不要删除动画对象
设置 fillMode
// 当动画执行完毕后不要删除动画对象
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
缺点: 无法与用户交互, 因为 frame 就没变

3> 介绍隐式代理
4> 不指定 fromValue 就是从当前位置开始动画
5> 注意:

  • 如果每次执行完毕动画都设置控件的 frame 为最终的 frame, 那么在连续多次点击的时候, 每次控件都要从上次结束出开始动画, 效果不好
  • 如果通过"动画执行完毕后不要删除动画对象", 动画比较流畅, 但是控件的 frame 始终没有变化, 将来可能会影响与用户交互

参考:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1. 创建动画对象
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
// 默认动画时间是0.25秒
animation.duration = 2.0;
// 设置动画代理
animation.delegate = self;

// 当动画执行完毕后不要删除动画对象
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;

// 2. 设置动画属性值
//animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];
animation.fromValue = [NSValue valueWithCGPoint:self.blueView.center];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];

// 3. 将动画添加到对应的layer 中
[self.blueView.layer addAnimation:animation forKey:@"animation1"];

// 动画执行完毕后设置控件的 center
//NSLog(@"★");
//self.blueView.center = CGPointMake(300, 50);
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(@"%@----", NSStringFromCGRect(self.blueView.frame));

NSLog(@"========%@", NSStringFromCGRect(self.blueView.layer.frame));

NSLog(@"========%@", NSStringFromCGPoint(self.blueView.layer.position));
//self.blueView.center = CGPointMake(300, 50);
}



//-------------------- 参考-----------------
// 1. 创建动画对象
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
// 默认动画时间是0.25秒
animation.duration = 1.0;
// 设置动画代理
animation.delegate = self;

// 当动画执行完毕后不要删除动画对象
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;

// 2. 设置动画属性值
//animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(200, 30)];
// animation.fromValue = [NSValue valueWithCGPoint:self.blueView.center];
//animation.toValue = [NSValue valueWithCGPoint:CGPointMake(240, 500)];
animation.toValue = [NSValue valueWithCGPoint:location];

// 3. 将动画添加到对应的layer 中

// 这样每次会添加一个新的动画
//self.blueView.layer addAnimation:animation forKey:nil] ;
// 注意: 如果下面的 key 指定了一个写死的 key,@"animation100",这样不会每次都添加一个新的动画了。
[self.blueView.layer addAnimation:animation forKey:@"animation100"];
// 动画执行完毕后设置控件的 center
//NSLog(@"★");
//self.blueView.center = CGPointMake(300, 50);

2.2 基本动画: 缩放动画
参考:

// 缩放动画
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 创建动画对象
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
// 设置动画属性
anim.fromValue = @(1.0f);
anim.toValue = @(0.7f);
// 设置重复次数
anim.repeatCount = 10;

// 将动画对象添加到 layer 中
[self.blueView.layer addAnimation:anim forKey:nil];
}

2.3 基本动画: 旋转动画
参考

 // 旋转动画
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
 
 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
 
 anim.duration = 3;
 anim.repeatCount = CGFLOAT_MAX;
 // 不要每次都设置起始度数
 //anim.fromValue = @(0);
 anim.toValue = @(M_PI * 2);
 
 [self.blueView.layer addAnimation:anim forKey:nil];
 }
  • 注意: 如果 [self.blueView.layer addAnimation:anim forKey:nil];没有指定 forKey, 那么每次都会添加一个新动画, 会越来越快
  • 解决:
    参考:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 判断如果已经有动画对象了, 就不再添加了
if ([self.blueView.layer animationForKey:@"anim1"] != nil) {
return;
}

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];

anim.duration = 3;
anim.repeatCount = CGFLOAT_MAX;
// 不要每次都设置起始度数
//anim.fromValue = @(0);
anim.toValue = @(M_PI * 2);

[self.blueView.layer addAnimation:anim forKey:@"anim1"];
}
  • 注意: 如果当动画正在执行的时候, 将程序退出到后台, 那么当程序再次进入前台的时候就不执行了。
  • 原因: 因为再次进入前台后动画已经被删除了。
  • 解决1: anim.removedOnCompletion = NO;
  • 问题: 当双击 home 键的时候, 动画不会暂停。
  • 解决:
    参考:
// 暂停
- (void)applicationWillResignActive:(UIApplication *)application {
ViewController *vc =  (ViewController *)self.window.rootViewController;
[vc pause];
}

// 恢复
- (void)applicationDidBecomeActive:(UIApplication *)application {
ViewController *vc =  (ViewController *)self.window.rootViewController;
[vc resume];
}

2.4 关键帧动画

  • 设置 values 属性
    参考:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
anim.duration = 3;
anim.removedOnCompletion = NO;
CGPoint p1 = CGPointMake(10, 10);
CGPoint p2 = CGPointMake(10, 110);
CGPoint p3 = CGPointMake(110, 110);
CGPoint p4 = CGPointMake(110, 10);
CGPoint p5 = CGPointMake(10, 10);
anim.values = @[[NSValue valueWithCGPoint:p1], [NSValue valueWithCGPoint:p2], [NSValue valueWithCGPoint:p3], [NSValue valueWithCGPoint:p4],[NSValue valueWithCGPoint:p5]];
[self.blueView.layer addAnimation:anim forKey:nil];
}
  • 设置 path 属性
    参考:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
anim.duration = 3;
anim.removedOnCompletion = NO;
anim.fillMode = kCAFillModeForwards;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 150, 150)];
anim.path = path.CGPath;

[self.blueView.layer addAnimation:anim forKey:nil];
}
  • 模拟"app 抖动"
  • 思路: 通过设置左右旋转实现
    参考:
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
 if ([self.blueView.layer animationForKey:@"shake"]) {
 return;
 }
 
 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
 anim.values = @[@(-M_PI / 36), @(M_PI / 36), @(-M_PI / 36)];
 anim.duration = 0.15;
 anim.repeatCount = CGFLOAT_MAX;
 [self.blueView.layer addAnimation:anim forKey:@"shake"];
 
 }
  • 组动画
    参考:
 // 动画组, 组动画
 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 {
 UITouch *touch = touches.anyObject;
 CGPoint location = [touch locationInView:touch.view];
 CAAnimationGroup *groupAnim = [[CAAnimationGroup alloc] init];
 
 // 位移
 CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:@"position"];
 anim1.toValue = [NSValue valueWithCGPoint:location];
 
 // 缩放
 CABasicAnimation *anim2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
 anim2.toValue = @(0.3);
 // 旋转
 CABasicAnimation *anim3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
 anim3.toValue = @(M_PI * 8);
 // 关键帧动画
 CAKeyframeAnimation *anim4 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(180, 150, 150, 150)];
 anim4.path = path.CGPath;
 groupAnim.animations = @[anim1, anim2, anim3, anim4];
 groupAnim.duration = 1.0;
 groupAnim.repeatCount = CGFLOAT_MAX;
 
 [self.blueView.layer addAnimation:groupAnim forKey:nil];
 }
  • 转场动画
    案例: 通过在 view 中放一个图片框, 从"控件库"中拖拽两个"轻扫手势"(拖到哪个控件上, 就表示应用到了哪个控件上, 打开看 storyboard 中的 xml 文件),然后为"轻扫手势"拖线连接处理程序, 在处理程序中实现"转场动画"。
    参考:
- (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {

// 创建一个转场动画对象
CATransition *anim = [[CATransition alloc] init];
// 设置过度类型
anim.type = @"cube";

if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
anim.subtype = kCATransitionFromRight;
} else {
anim.subtype = kCATransitionFromLeft;
}
[self.imgViewIcon.layer addAnimation:anim forKey:nil];
}

//-------------------------------------------------------
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imgViewIcon;
@property (nonatomic, assign) int index;
@end

@implementation ViewController

- (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {
// 创建一个转场动画对象
CATransition *anim = [[CATransition alloc] init];
// 设置过度类型
anim.type = @"cube";

if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
anim.subtype = kCATransitionFromRight;
self.index--;
} else {
anim.subtype = kCATransitionFromLeft;
self.index++;
}
if (self.index < 0) {
self.index = 4;
}
if (self.index > 4) {
self.index = 0;
}
NSString *imgName = [NSString stringWithFormat:@"%d", (self.index + 1)];
self.imgViewIcon.image = [UIImage imageNamed:imgName];
[self.imgViewIcon.layer addAnimation:anim forKey:nil];
}

//-------------- 通过 uiview 实现转场动画-----------
- (IBAction)swipeLeft:(UISwipeGestureRecognizer *)sender {
UIViewAnimationOptions option;
if (sender.direction == UISwipeGestureRecognizerDirectionLeft) {
option = UIViewAnimationOptionTransitionCurlUp;
self.index--;
} else {
option = UIViewAnimationOptionTransitionCurlDown;
self.index++;
}
if (self.index < 0) {
self.index = 4;
}
if (self.index > 4) {
self.index = 0;
}
NSString *imgName = [NSString stringWithFormat:@"%d", (self.index + 1)];
self.imgViewIcon.image = [UIImage imageNamed:imgName];
[UIView transitionWithView:self.view duration:1.0 options:option animations:^{
// 在转场动画的过程中还可以执行其他的动画
} completion:^(BOOL finished) {
// 动画执行完毕后要做的事情
}];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,321评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 4,996评论 5 13
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 2,967评论 0 21
  • 在iOS实际开发中常用的动画无非是以下四种:UIView动画,核心动画,帧动画,自定义转场动画。 1.UIView...
    请叫我周小帅阅读 3,000评论 1 23
  • 天龙八部无人不知,大理苍山也随其名满天下。小说已经久远,记不清具体细节,但点仓派武功的名头多少有些记忆,苍山在我看...
    林夕风起阅读 593评论 0 3