iOS Developer - AnimationiOS 核心动画iOS Developer - CALayer

6.2 核心动画->3.0 核心动画 & UIVi

2016-07-25  本文已影响295人  蓝田_Loto

本文并非最终版本,如果想要关注更新或更正的内容请关注文集,联系方式详见文末,如有疏忽和遗漏,欢迎指正。


本文相关目录:
===================== 所属文集:6.0 图形和多媒体 =====================
6.2 核心动画->1.0 CALayer的简介
6.2 核心动画->1.1 CALayer的基本属性
6.2 核心动画->1.2 CALayer的创建 & 非根layer的隐式动画
6.2 核心动画->2.0 Core Animation(核心动画)
6.2 核心动画->3.0 核心动画 & UIView动画
6.2 核心动画->4.0 常用动画效果
===================== 所属文集:6.0 图形和多媒体 =====================


核心动画代码示例:

#import "ViewController.h"

@interface ViewController ()
@property(weak, nonatomic) IBOutlet UIView *redView;
@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  CABasicAnimation *anim = [CABasicAnimation animation];

  anim.keyPath = @"position";

  anim.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 400)];

  // 注意:取消反弹代码必须放在图层添加动画之前。
  anim.removedOnCompletion = NO;

  // 设置保存动画的最新状态,图层会保持显示动画执行后的状态
  anim.fillMode = kCAFillModeForwards;

  //(不需要遵守协议)设置代理,动画执行完毕后会调用delegate的animationDidStop:finished:
  anim.delegate = self;

  [_redView.layer addAnimation:anim forKey:nil];
}

#pragma mark - 动画完成的时候调用
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
  // 打印图层的位置的CGPoint
  NSLog(@"动画完成:%@", NSStringFromCGPoint(_redView.layer.position));
}

- (void)viewDidLoad {
  [super viewDidLoad];
  // 打印图层的位置的CGPoint
  NSLog(@"原始位置:%@", NSStringFromCGPoint(_redView.layer.position));
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

@end

运行效果:

核心动画.gif

打印结果:

核心动画[1112:36378] 原始位置:{135, 122}
核心动画[1112:36378] 动画完成:{135, 122}

结论:核心动画一切都是假象,并不会真实的改变图层的属性值,如果以后做动画的时候,不需要与用户交互,通常用核心动画(如:转场动画)。


UIView动画代码示例:

#import "ViewController.h"

@interface ViewController ()
@property(weak, nonatomic) IBOutlet UIView *redView;
@end

@implementation ViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

  [UIView animateWithDuration:0.25
      animations:^{
        _redView.layer.position = CGPointMake(150, 400);
      }
      completion:^(BOOL finished) {
        // 动画完成的时候,打印图层的位置的CGPoint
        NSLog(@"动画完成:%@", NSStringFromCGPoint(_redView.layer.position));
      }];
}

- (void)viewDidLoad {
  [super viewDidLoad];
  // 打印图层的位置的CGPoint
  NSLog(@"原始位置:%@", NSStringFromCGPoint(_redView.layer.position));
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

@end

结论:UIView动画必须通过修改属性的真实值,才有动画效果。

运行效果:


核心动画.gif

打印结果:

UIView动画[1258:43954] 原始位置:{133, 109}
UIView动画[1258:43954] 动画完成:{150, 400}

结论:UIView动画必须通过修改属性的真实值,才有动画效果。


核心动画案例 - 钟表动画

问题描述:通过 NSTimer 实现的动画可能造成卡顿、不连贯的情况(NSTimer 不准确)

解决方法:通过 CADisplayLink 来解决,CADisplayLink 表示"显示连接", 与显示器的刷新频率相关。

CADisplayLink:

- CADisplayLink是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右
- CADisplayLink是一个计时器,可以使绘图代码与视图的刷新频率保持同步,而NSTimer无法确保
计时器实际被触发的准确时间

使用方法:

- 定义CADisplayLink并制定触发调用方法
- 将显示链接添加到主运行循环队列

代码示例:

#define kClockW _clockView.bounds.size.width

#define angle2radion(a) ((a) / 180.0 * M_PI) // 角度转弧度
#define perSecondA 6                        // 一秒钟秒针转6°
#define perMinuteA 6                        // 一分钟分针转6°
#define perHourA 30                          // 一小时时针转30°

// 每分钟时针转多少度
#define perMinuteHour A 0.5

#import "ViewController.h"

@interface ViewController ()
@property(weak, nonatomic) IBOutlet UIImageView *clockView;
@property(nonatomic, weak) CALayer *secondLayer; // 秒针
@property(nonatomic, weak) CALayer *minuteLayer; // 分针
@property(nonatomic, weak) CALayer *hourLayer;   // 时针
@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  [self setUpHourLayer];   // 添加时针
  [self setUpMinuteLayer]; // 添加分针
  [self setUpSecondLayer]; // 添加秒针

 // 方式1:NSTimer定时器
  [NSTimer scheduledTimerWithTimeInterval:1
                                   target:self
                                 selector:@selector(timeChange)
                                 userInfo:nil
                                  repeats:YES];

  [self timeChange];


  // 方法2:CADisplayLink(link默认是1/60 秒执行一次)
  CADisplayLink *link =
      [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
  //执行定时器 把定时器放在主运行循环中执行
  [link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}

// 获取当前的系统的时间
- (void)timeChange {
  // 获取当前日历对象
  NSCalendar *calendar = [NSCalendar currentCalendar];

  // 获取日期的组件:年月日时分秒
  // components:需要获取的日期组件  fromDate:获取哪个日期的组件
  // 经验:以后枚举中有移位运算符,通常一般可以使用并运算(|)
  NSDateComponents *cmp =
      [calendar components:NSCalendarUnitSecond | NSCalendarUnitMinute |
                           NSCalendarUnitHour
                  fromDate:[NSDate date]]; // 获取当前日期的组件

  // 获取秒
  NSInteger second = cmp.second;
  // 获取分
  NSInteger minute = cmp.minute;
  // 获取小时
  NSInteger hour = cmp.hour;

  // 计算秒针转多少度
  CGFloat secondA = second * perSecondA;
  // 计算分针转多少度
  CGFloat minuteA = minute * perMinuteA;
  // 计算时针转多少度
  CGFloat hourA = hour * perHourA + minute * perMinuteHourA;

  // 旋转秒针
  _secondLayer.transform =
      CATransform3DMakeRotation(angle2radion(secondA), 0, 0, 1);
  // 旋转分针
  _minuteLayer.transform =
      CATransform3DMakeRotation(angle2radion(minuteA), 0, 0, 1);
  // 旋转小时
  _hourLayer.transform =
      CATransform3DMakeRotation(angle2radion(hourA), 0, 0, 1);
}

#pragma mark - 添加秒针
- (void)setUpSecondLayer {
  // 创建秒针
  CALayer *secondL = [CALayer layer];

  // 秒针背景色
  secondL.backgroundColor = [UIColor redColor].CGColor;

  // 设置锚点
  secondL.anchorPoint = CGPointMake(0.5, 1);
  // 设置位置
  secondL.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
  // 设置 bounds
  secondL.bounds = CGRectMake(0, 0, 1, kClockW * 0.5 - 20);

  // 添加到 view 上
  [_clockView.layer addSublayer:secondL];

  _secondLayer = secondL;
}

#pragma mark - 添加分针
- (void)setUpMinuteLayer {
  // 创建分针
  CALayer *layer = [CALayer layer];

  // 分针背景色
  layer.backgroundColor = [UIColor blackColor].CGColor;

  // 设置锚点
  layer.anchorPoint = CGPointMake(0.5, 1);
  // 设置位置
  layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
  // 设置 bounds
  layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 20);
  // 设置圆角半径
  layer.cornerRadius = 4;

  [_clockView.layer addSublayer:layer];

  _minuteLayer = layer;
}

#pragma mark - 添加时针
- (void)setUpHourLayer {
  // 创建时针
  CALayer *layer = [CALayer layer];

  layer.backgroundColor = [UIColor blackColor].CGColor;

  // 设置锚点
  layer.anchorPoint = CGPointMake(0.5, 1);

  layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);

  layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 40);

  layer.cornerRadius = 4;

  [_clockView.layer addSublayer:layer];

  _hourLayer = layer;
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
  // Dispose of any resources that can be recreated.
}

@end

运行效果:

钟表动画.gif

本文源码 Demo 详见 Github
https://github.com/shorfng/iOS_6.0_Graphics_and_multimedia.git




作者:蓝田(Loto)
出处: 简书

如果你觉得本篇文章对你有所帮助,请点击文章末尾下方“喜欢”
如有疑问,请通过以下方式交流:
评论区回复微信(加好友请注明“简书+称呼”)发送邮件shorfng@126.com



本文版权归作者和本网站共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

上一篇 下一篇

猜你喜欢

热点阅读