你所不知道的CALayer隐式动画及事务

浅谈CALayer的隐式动画及事务

一、前言

本文是为了后续直播App送礼大动画实战演练做铺垫, 浅谈CALayer的隐式动画及事务.

全文主要涉及以下问题:

  • CALayer图层的定义
  • 何为隐式动画?
  • 所有CALayer都有隐私动画吗? UIView的backing layer呢?
  • 哪些对象遵循CAAction协议
  • 动画事务是什么?

二、 CALayer

CALayer图层, 是数据模型, 数据对象. 对于iOS平台, 一个UIView视图在展示之前, 系统都会为其创建一个支持图层(backing layer), 其中就储存了View外貌样式的表现内容, 比如图层通过contents属性来管理bitmap位图, 从而充当位图的容器.

Backing layer的delegate(CALayerDelegate)就是该图层所属的view对象.

三、 隐式动画

我们平常都肯定使用过显式动画, 但我们比较少见到的隐式动画(implicit animation)又是什么.

从Core Animation Guide官方指南中, 并没有找到隐式动画的明确定义, 仅提及了以下相关内容:

  1. 直接更改图层CALayer的属性就会触发隐式动画, 但是修改UIView对象支持图层(backing layer)的动画属性是不会发生隐式动画的, 因为UIView默认禁止了backing layer的隐式动画, 所以对backing layer属性的修改在UIView上的反应是直接变化, 没有平滑过度的动画效果
  2. 隐式动画会使用当前动画事务的参数默认值来执行动画
  3. 隐式动画会直接更改了layer模型中的值, 而显式动画不会(在动画执行完后, 会根据layer模型的属性进行"还原", 所以我们在添加动画后需要手动修改layer的属性来确保动画完成时, 图层能够"还原"到动画结束时的位置! 只有presentationLayer中的值会随着动画执行不断改变.)
  4. 隐式动画执行过程中无法直接被移除, 而显式动画可以通过实例方法removeAnimationForKey:或removeAllAnimations来直接移除
  5. Core Animation通过 遵循CAAction协议的对象 来实现隐式动画

此处来一发示例, 帮助我们理解一下上方第1点:

/*
 现有UIView对象 viewA, 以及一个CALayer对象 layerB, 我们把 layerB 添加到 
 viewA 的 layer(这个就叫backing layer) 上.
*/

// 对backing layer进行属性修改, 不会发生隐式动画, 而是由之前的颜色直接变成红色   
viewA.layer.backgroundColor = [UIColor redColor].CGColor;

// layerB作为viewA.layer的子图层, 会发生隐式动画, layerB的颜色会由原先的颜色平滑过渡到蓝色
layerB.backgroundColor = [UIColor blueColor].CGColor;

至此, 想必我们已经清楚CALayer何时会发生隐式动画了, 但后半句中说UIView的backing layer不会有隐式动画又是何解?

这里需要关联第5点中提到的遵循CAAction协议的对象

实际上, CAAnimation及其派生类, 如CABasicAnimation、CASpringAnimation, 以及CATransition都遵循并实现了 CAAction 协议中的方法. 当CALayer的动画属性改变时就会去查找匹配的CAAniamtion对象来执行动画. 其查找流程用代码形式表示如下:

/*
1. 若返回遵循 CAAction 协议对象, 则用其执行动画(runActionForKey:object:arguments:)
2. 若该方法返回nil, 不执行隐式动画, 直接更新属性
*/
- (id<CAAction>)searchActionForLayer:(CALayer *)layer forKey:(NSString *)key {
    id<CAAction> action = nil;
    // 先问代理该key对应的Action, 如果返回不为nil, 则已找到; 如果返回nil, 则继续执行查找逻辑; 如果返回NSNull对象则停止剩余查找逻辑, 最终返回NSNull
    if ([layer.delegate respondsToSelector:@selector(actionForLayer:forKey:)]) {
        id<CAAction> action = [layer.delegate actionForLayer:layer forKey:key];
        if ([NSNull null] == action) {
            return nil;
        }
        else if (action) {
            return action;
        }
    }
    if (!action) {
        action = [self actionForKey:key in:layer.actions];
    }
    if (!action) {
        for (NSDictionary<NSString*, id<CAAction>> *actions in layer.style) {
            if ((action = [self actionForKey:key in:actions])) {
                break;
            }
        }
    }
    if (!action) {
        action = [[layer class] defaultActionForKey:key];
    }
    return action;
}

- (id<CAAction>)actionForKey:(NSString *)key in:(NSDictionary<NSString*, id<CAAction>> *)actions {
    for (NSString *actionKey in actions.allKeys) {
        if ([actionKey isEqualToString:key]) {
            return actions[actionKey];
        }
    }
    return nil;
}

UIView作为backing layer的delegate(as CALayerDelegate), 实现了-actionForLayer:forKey方法; 当不处于动画block范围内时, 该方法返回nil; 否则, 返回对应的动画对象. 关联上方函数注释若该方法返回nil, 不执行隐式动画, 直接更新属性, 这就是为什么UIView的backing layer不会有隐式动画.

四、 动画事务

动画事务, 也类似于数据库事务, 用于组合某个逻辑里边的一系列操作. Core Animation会自动为我们某个图层的单个或多个显式动画、隐式动画创建隐式事务. 当然, 我们也可以通过 CATransaction 的类方法, 来显式创建事务.

// 显式事务
[CATransaction begin];
[CATransaction setValue:@(0.3) forKey:kCATransactionAnimationDuration];
layer.opacity = 0.0;
[CATransaction commit];

// UIView提供用来做动画的类方法等价于上方显式创建动画, 因为这些类方法内部就调用了CATransaction的+begin, +commit方法
[UIView animateWithDuration:0.3 animations:^{
    layer.opacity = 0.0;
}];

当runloop开始一次新的循环时, Core Animation就会开启一个事务, 在本次循环中的所有动画操作, 包括我们显示创建的事务都会被嵌套其中, 直至本次runloop循环结束之时, 再一块提交进行动画. 比如下方代码:

// layerA、layerB、layerC均为寄宿/单独图层(hosted layer/standalone layer)

// --- runloop 新的循环开始 ---
[CATransaction begin]; // Core Animation 开始的新事务

// -- 本次循环中, 我们涉及的动画操作 start -- 

// 修改单独图层属性, 将以最外层CATransaction的动画参数发起隐式动画, 动画时间为当前所在事务的动画时间, 即默认的0.25秒
layerA.backgroundColor = [UIColor redColor].CGColor; 

// 创建显式事务, 嵌套事务
[CATransaction begin];
[CATransaction setValue:@(1) forKey:kCATransactionAnimationDuration];
// 当前事务中的隐式动画, 执行时间为1秒
layerB.position = CGPoint(110, 119);
[CATransaction commit];

// 实际也相当于嵌套事务
[UIView animateWithDuration:0.5 animations:^{
    layerC.opacity = 0.0;
}];

// -- 本次循环中, 我们涉及的动画操作 end -- 

[CATransaction commit]; // Core Animation 提交所有事务
// --- runloop 循环结束 ---

利用事务, 禁止动画发生:

[CATransaction begin];
// 设置 kCATransactionDisableActions 为 false
[CATransaction setValue:@(false)
                 forKey:kCATransactionDisableActions];

// UIView Animation显示动画失效
[UIView animateWithDuration:0.33
                 animations:^{
                    // 单独的图层layerA
                    layerA.backgroundColor = [UIColor redColor].CGColor;
                 }];

// 单独的图层layerB, 隐式动画失效
layerB.opacity = 0.0;

[CATransaction commit];

五、推荐资料

本文提及内容仅为Core Animation的冰山一角, 如果想深入了解iOS动画, 可以尝试阅读一下下方资料, 定会有一番收获!

Core Animation Guide
iOS Core Animation
CATransaction In Depth

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

推荐阅读更多精彩内容