iOS核心动画(一)

核心动画(CoreAnimation)作为苹果官方基于OpenGLES封装的一套动画框架。使用起来相对于OpenGLES简单的多。也更容易被绝大多数OC开发者使用。

先来看一下相关框架图


image.png

日常中,我们基于UIView的layer做动画要多一些。我们先看一下CALayer。

CALayer.

为什么叫CALayer?CoreAnimationLayer。在命名里就能看得出来,CALayer的核心作用就是来处理动画。

UIView与CALayer的关系

首先,UIView是CALayer的delegate。
其次,UIView与CALayer承载不同的功能,两者的分工自然应该是明确的(职责分离)。
最后,CALayer不清楚响应链关系,所以CALayer无法用来处理交互事件。而UIView继承自UIResponser,所以由UIView处理事件交互。而UIView作为CALayer的代理,自然也可以实现一些简单的CALayer的方法。但要实现稍微复杂些的动画效果,就需要借助CALayer,如:

  • 阴影,圆角,带颜色的边框
  • 3D变换
  • 非矩形范围
  • 透明遮罩
  • 多级非线性动画
对于layer的树级关系:
  • 模型树( layer tree):程序中接触最频繁。模型树的对象是模型对象,储存着动画的目标值。当你修改layer的属性时,便是通过模型树上的对象。
  • 呈现树(presentation tree):包含正在运行中的动画的动态值。与模型树不同,呈现树始终存储着layer在屏幕当前的状态值。呈现树无法修改,只读。可以通过读取当前值,来做一些其他处理。
  • 渲染树(render tree):执行实际的动画,为Core Animation私有。
image.png

接下来我们来做一个简单的动画:

  CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(100, 100, 100, 100);
    layer.backgroundColor = [UIColor orangeColor].CGColor;
    _layer = layer;
    [self.view.layer addSublayer:layer];
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"position.y";
    animation.toValue = @400;
    animation.duration = 1;
    
    [_layer addAnimation:animation forKey:nil];
}

我们会发现动画执行完毕后,又回到了初始位置。为什么会这样?

还记得上面中我们提到过layerTree跟presentationTree吧。其实在执行动画的时候会有两个图层,一个是layer层,一个是presentation层。当动画开始前,会先把layer层隐藏,让presentation层做动画。动画做完后,presentation层移除,layer层出现。所以其实我们的视图的layer层根本没有发生变化。怎么解决呢?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"position.y";
    animation.toValue = @400;
    animation.duration = 1;
    
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    
    [_layer addAnimation:animation forKey:nil];
}

我们仅仅多加了两行代码:
第一行代码的意思是:当动画完成后,不把presentation层从render树中移除(默认是移除的)。
第二行代码的意思是:当动画结束后,layer层会把状态同步到presentation层。

如果想做连续动画

一:可以设置一下代理
animation.delegate = self;

在下面这个方法里,监听动画完成,然后执行下一个动画。

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag

不过这里有两个坑点:
坑点一:这个delegate默认把动画对象retain了一份。所以你的animation对象跟代理方法里的anim不是同一个对象
坑点二:这个delegate是个strong类型,动画执行完毕后,需要额外处理一下才能把代理释放。这里有两个方式:
方式一:在动画完成的代理里,把所有的动画全部移除。
方式二:继承CABasicAnimation后,重写代理方法。

另外CAAnimation默认是有隐式动画的,动画时间为0.25s。可以通过CATransaction设置隐式动画的时间。代码如下:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    CABasicAnimation *animation = [CABasicAnimation animation];
    _animation = animation;
    animation.keyPath = @"position.y";
    animation.toValue = @400;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    [CATransaction begin];
    [CATransaction setAnimationDuration:10];
    [_layer addAnimation:animation forKey:nil];
    [CATransaction commit];
}
二:通过动画组来实现
CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
    animation1.fromValue = @(1.0);
    animation1.toValue = @(1.5);
    
    CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
    animation2.fromValue = @(1.0);
    animation2.toValue = @(1.5);
    
    CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"position"];
    animation3.fromValue = [NSValue valueWithCGPoint:_ballLayer.position];
    animation3.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.frame.size.width - (30 * 1.3)/2.0 , _ballLayer.position.y - 200)];
    
    
    CAAnimationGroup *anima = [CAAnimationGroup animation];
    anima.animations = @[animation1, animation2,animation3];
    anima.duration = 1.0;
    anima.delegate = (id)self;
    anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    anima.fillMode = kCAFillModeForwards;
    anima.removedOnCompletion = NO;
    
    [_ballLayer addAnimation:anima forKey:@"group_launch"];
}
额外补充两点:

一、什么是hitTest

  • (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    point : 在接收器的局部坐标系中指定的点。
    event : 系统保证调用此方法的事件。如果从事件处理代码外部调用此方法,则可以指定nil。
    returnValue : 视图对象是当前视图和包含点的最远的后代。如果点完全位于接收方的视图层次结构之外,则返回nil。

二:传递机制如下:


image.png

对于更多的动画效果可参考:https://www.jianshu.com/p/94f047efee6d

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 在iOS实际开发中常用的动画无非是以下四种:UIView动画,核心动画,帧动画,自定义转场动画。 1.UIView...
    请叫我周小帅阅读 8,415评论 1 23
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,903评论 1 32
  • 记录一下学习的笔记 核心动画 核心动画基本概念 基本动画 关键帧动画 动画组 转场动画 Core Animatio...
    耿杰阅读 4,711评论 0 1
  • 图层的树状结构 Core Animation是一个复合引擎,它的职责就是尽可能快地组合屏幕上不用的可视内容,这个内...
    Rathen阅读 3,085评论 0 0
  • 目录 ** UIView 动画 ** ** Core Animation ** ** FaceBook POP动画...
    方向_4d0d阅读 5,660评论 0 3

友情链接更多精彩内容