研究 Core Animation 已经有段时间了,关于 Core Animation,网上没什么好的介绍。苹果网站上有篇专门的总结性介绍,但是似乎原理性的东西不多,看得人云山雾罩,感觉,写那篇东西的人,其实是假设读的人了解界面动画技术的原理的。今天有点别的事情要使用 Linux,忘掉了 SSH 的密码,没办法重新设 SSH,结果怎么也想不起来怎么设 SSH 远程登陆了,没办法又到网上查了一遍,太浪费时间了,痛感忘记记笔记是多么可怕的事情。鉴于 Core Animation 的内容实在是非常繁杂,应用的 Obj-C 语言本身的特性也很多,所以写个备忘录记录一下,懂的人看了后如果发现了错误,还不吝指教。

  1. UIView 是 iOS 系统中界面元素的基础,所有的界面元素都继承自它。它本身完全是由 CoreAnimation 来实现的(Mac 下似乎不是这样)。它真正的绘图部分,是由一个叫 CALayer(Core Animation Layer)的类来管理。UIView 本身,更像是一个 CALayer 的管理器,访问它的跟绘图和跟坐标有关的属性,例如 frame,bounds 等等,实际上内部都是在访问它所包含的 CALayer 的相关属性。

  2. UIView 有个 layer 属性,可以返回它的主 CALayer 实例,UIView 有一个 layerClass 方法,返回主 layer 所使用的类,UIView 的子类,可以通过重载这个方法,来让 UIView 使用不同的 CALayer 来显示,例如通过

    /**/- (class) layerClass {
    return ([CAEAGLLayer class]);
    }

    使某个 UIView 的子类使用 GL 来进行绘制。

  3. UIView 的 CALayer 类似 UIView 的子 View 树形结构,也可以向它的 layer 上添加子 layer,来完成某些特殊的表示。例如下面的代码:

    grayCover = [[CALayer alloc] init];
    grayCover.backgroundColor = [[[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];
    [self.layer addSubLayer: grayCover];

    会在目标 View 上敷上一层黑色的透明薄膜。

  4. UIView 的 layer 树形在系统内部,被系统维护着三份 copy(这段理解有点吃不准)。

    • 第一份,逻辑树,就是代码里可以操纵的,例如更改 layer 的属性等等就在这一份。
    • 第二份,动画树,这是一个中间层,系统正在这一层上更改属性,进行各种渲染操作。
    • 第三份,显示树,这棵树的内容是当前正被显示在屏幕上的内容。
      这三棵树的逻辑结构都是一样的,区别只有各自的属性。
  5. 动画的运作
    UIView 的主 layer 以外(我觉得是这样),对它的 subLayer,也就是子 layer 的属性进行更改,系统将自动进行动画生成,动画持续时间有个缺省时间,个人感觉大概是 0.5 秒。在动画时间里,系统自动判定哪些属性更改了,自动对更改的属性进行动画插值,生成中间帧然后连续显示产生动画效果。

  6. 坐标系系统(对 position 和 anchorPoint 的关系还是犯晕)
    CALayer 的坐标系系统和 UIView 有点不一样,它多了一个叫 anchorPoint 的属性,它使用 CGPoint 结构,但是值域是 0~1,也就是按照比例来设置。这个点是各种图形变换的坐标原点,同时会更改 layer 的 position 的位置,它的缺省值是 {0.5, 0.5},也就是在 layer 的中央。

    layer.anchorPoint = CGPointMake(0.f, 0.f);

    如果这么设置,layer 的左上角就会被挪到原来的中间的位置,
    加上这样一句就好了

    layer.position = CGPointMake(0.f, 0.f);
  7. 真实例子的分析
    这是 iPhone 上 iBook 翻页的效果,假设每一页都是一个 UIView,我觉得一个页面是贴了俩个 Layer,文字 Layer 显示正面的内容,背面 layer 用文字 layer 的快照做 affine 翻转,贴在文字 layer 的后面。因为 Layer 可以设置显示阴影,也许后面的阴影效果没有使用单独的一个 layer 来显示。至于这个曲面效果,我查了很多资料也没有结果,估计是使用了 GL 的曲面绘图?

  8. 最后一个让人恶心的
    layer 可以设置圆角显示,例如 UIButton 的效果,也可以设置阴影显示,但是如果 layer 树中的某个 layer 设置了圆角,树中所有 layer 的阴影效果都将显示不了了。如果既想有圆角又想要阴影,好像只能做两个重叠的 UIView,一个的 layer 显示圆角,一个的 layer 显示阴影...

CALayer 属于 Core Animation 部分的内容,比较重要而不太好理解。以下是园子中看到的一篇文章的摘录:

  1. UIView 是 iOS 系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由 CoreAnimation 来实现的。它真正的绘图部分,是由一个 CALayer 类来管理。UIView 本身更像是一个 CALayer 的管理器,访问它的跟绘图和跟坐标有关的属性,例如 frame,bounds 等,实际上内部都是在访问它所包含的 CALayer 的相关属性。

  2. UIView 有个重要属性 layer,可以返回它的主 CALayer 实例。
    要访问层,读取 UIView 实例的 layer 属性

    CALayer *layer = myView.layer

    所有从 UIView 继承来的对象都继承了这个属性。这意味着你可以转换、缩放、旋转,甚至可以在 Navigation bars,Tables,Text boxes 等其它的 View 类上增加动画。每个 UIView 都有一个层,控制着各自的内容最终被显示在屏幕上的方式。
    UIView 的 layerClass 方法,可以返回主 layer 所使用的类,UIView 的子类可以通过重载这个方法,来让 UIView 使用不同的 CALayer 来显示。代码示例:

    /**/- (class)layerClass {
    return ([CAEAGLLayer class]);
    }

    上述代码使得某个 UIView 的子类使用 GL 来进行绘制。

  3. UIView 的 CALayer 类似 UIView 的子 View 树形结构,也可以向它的 layer 上添加子 layer,来完成某些特殊的表示。即 CALayer 层是可以嵌套的。
    示例代码:

    grayCover = [[CALayer alloc] init];
    grayCover.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];
    [self.layer addSubLayer:grayCover];

    上述代码会在目标 View 上敷上一层黑色透明薄膜的效果。

  4. UIView 的 layer 树形在系统内部,被维护着三份 copy。分别是逻辑树,这里是代码可以操纵的;动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;显示树,其内容就是当前正被显示在屏幕上得内容。

  5. 动画的运作:对 UIView 的 subLayer(非主 Layer)属性进行更改,系统将自动进行动画生成,动画持续时间的缺省值似乎是 0.5 秒。

  6. 坐标系统:CALayer 的坐标系统比 UIView 多了一个 anchorPoint 属性,使用 CGPoint 结构表示,值域是 0~1,是个比例值。这个点是各种图形变换的坐标原点,同时会更改 layer 的 position 的位置,它的缺省值是 {0.5,0.5},即在 layer 的中央。

    layer.anchorPoint = CGPointMake(0.f,0.f);

    如果这么设置,只会将 layer 的左上角被挪到原来的中间位置,必须加上这一句:

    layer.position = CGPointMake(0.f,0.f);

    最后:
    layer 可以设置圆角显示(cornerRadius),也可以设置阴影(shadowColor)。但是如果 layer 树中某个 layer 设置了圆角,树种所有 layer 的阴影效果都将不显示了。因此若是要有圆角又要阴影,变通方法只能做两个重叠的 UIView,一个的 layer 显示圆角,一个 layer 显示阴影。

  7. 渲染:当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以调用 setNeedsDisplay 方法来重绘显示。

    [gameLayer setNeedsDisplay];

    若要重绘部分屏幕区域,请使用 setNeedsDisplayInRect: 方法,通过在 CGRect 结构的区域更新:

    [gameLayer setNeedsDisplayInRect:CGRectMake(150.0,100.0,50.0,75.0)];

    如果是用的 Core Graphics 框架来执行渲染的话,可以直接渲染 Core Graphics 的内容。用 renderInContext: 来做这个事。

    [gameLayer renderInContext:UIGraphicsGetCurrentContext()];
  8. 变换:要在一个层中添加一个 3D 或仿射变换,可以分别设置层的 transform 或 affineTransform 属性。

    characterView.layer.transform = CATransform3DMakeScale(-1.0,-1.0,1.0);
    CGAffineTransform transform = CGAffineTransformMakeRotation(45.0);
    backgroundView.layer.affineTransform = transform;
  9. 变形:Quartz Core 的渲染能力,使二维图像可以被自由操纵,就好像是三维的。图像可以在一个三维坐标系中以任意角度被旋转,缩放和倾斜。CATransform3D 的一套方法提供了一些魔术般的变换效果。