美文网首页
iOS核心动画(一)

iOS核心动画(一)

作者: __拼搏__ | 来源:发表于2019-08-25 18:43 被阅读0次
核心动画(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

相关文章

网友评论

      本文标题:iOS核心动画(一)

      本文链接:https://www.haomeiwen.com/subject/stfkectx.html