【译】iOS CALayer 官方文档(1)

1,153 阅读32分钟

CAMediaTiming

 CAMediaTiming 协议由图层(CALayer)和动画(CAAnimation)实现,它为分层计时系统(hierarchical timing system)建模,每个对象描述了从其父对象的时间值到本地时间的映射。

 绝对时间被定义为 mach time 转换成秒。为了方便查询当前绝对时间,提供了 CACurrentMediaTime 函数。

 从父时间到本地时间的转换分为两个阶段:

  1. 转换为 “活动的本地时间”(active local time)。这包括对象在父时间轴中出现的时间点,以及它相对于父级播放的速度。
  2. 从活动时间转换为 “基本本地时间”(basic local time)。时序模型允许对象重复其基本持续时间多次,并可以选择在重复播放之前进行播放。
@protocol CAMediaTiming
...
@end

CACurrentMediaTime

 返回当前的绝对时间,以秒为单位。

CFTimeInterval CACurrentMediaTime(void);

 Return Value: 通过调用 mach_absolute_time() 并将结果转换为秒而得出的 CFTimeInterval。

Animation Start Time(动画开始时间)

beginTime

 指定 receiver 相对于其父对象的开始时间(如果适用),预设为 0。

/* The begin time of the object, in relation to its parent object, if applicable. Defaults to 0. */
@property CFTimeInterval beginTime;

 对象的开始时间(相对于其父对象)(如果适用)。预设为 0。

timeOffset

 指定活动的本地时间中的附加时间偏移,预设为 0。

/* Additional offset in active local time. 
 * i.e. to convert from parent time tp to active local time t: t = (tp - begin) * speed + offset.
 * One use of this is to "pause" a layer by setting 'speed' to zero and 'offset' to a suitable value. 
 * Defaults to 0. 
 */
@property CFTimeInterval timeOffset;

 活动的本地时间增加的偏移量。例如;从父时间 tp 转换为活动的本地时间 t:t = (tp - begin) * speed + offset。一种用法是通过将 speed 设置为零并将 offset 设置为合适的值来暂停("pause")layer。预设为 0。

Repeating Animations(重复动画)

repeatCount

 确定动画将重复的次数。

/* The repeat count of the object. May be fractional. Defaults to 0. */
@property float repeatCount;

 可能是分数(类型是 float)。如果 repeatCount 为 0,则将其忽略。预设值为 0。如果同时指定了 repeatDuration 和 repeatCount,则行为未定义。

repeatDuration

 确定动画将重复多少秒。(对象的重复持续时间。预设为 0。)

/* The repeat duration of the object. Defaults to 0. */
@property CFTimeInterval repeatDuration;

 预设值为 0。如果 repeatDuration 为 0,则将其忽略。如果同时指定了 repeatDuration 和 repeatCount,则行为是不确定的。

Duration and Speed(持续时间和速度)

duration

 指定动画的基本持续时间(以秒为单位),默认为 0。

/* The basic duration of the object. Defaults to 0. */
@property CFTimeInterval duration;

 对象的基本持续时间。预设为 0。

speed

 指定时间如何从父时间空间映射到 receiver 的时间空间。

/* The rate of the layer. Used to scale parent time to local time, 
 * e.g. if rate is 2, local time progresses twice as fast as parent time.
 * Defaults to 1. 
 */
@property float speed;

 例如,如果 speed 为 2.0,则本地时间的进度是父时间的两倍。默认为 1.0。

 layer 的速率。用于将父时间缩放为本地时间,例如如果比率为 2,则本地时间的进度是父时间的两倍。

Playback Modes(播放模式)

autoreverses

 确定 receiver 在完成时是否反向播放。

/* When true, the object plays backwards after playing forwards. Defaults to NO. */
@property BOOL autoreverses;

 如果为 true,则对象在向前播放后向后播放。默认为 NO。

fillMode

 确定 receiver 的 presentation 在其有效期限完成后是否被冻结或删除。可能的值在 Fill Modes 中说明。默认值为 kCAFillModeRemoved。

/* Defines how the timed object behaves outside its active duration.
 * Local time may be clamped to either end of the active duration, 
 * or the element may be removed from the presentation. 
 * The legal values are 'backwards', 'forwards', 'both' and 'removed'. 
 * Defaults to 'removed'. 
 */
@property(copy) CAMediaTimingFillMode fillMode;

 定义 timed object 在其活动持续时间之外的行为。本地时间可以固定在活动持续时间的任一端,或者可以从 presentation 中删除该元素。合法值是 backwards、forwards、both、和 removed。默认为 removed。

Fill Modes

 这些常数确定了 timed object 的活动持续时间完成后的行为。它们与 fillMode 属性一起使用。

typedef NSString * CAMediaTimingFillMode NS_TYPED_ENUM;
/* `fillMode' options. */

CA_EXTERN CAMediaTimingFillMode const kCAFillModeForwards   API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeBackwards  API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeBoth       API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CAMediaTimingFillMode const kCAFillModeRemoved    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
  • kCAFillModeForwards: 动画完成后,receiver 在其最终状态下仍然可见。
  • kCAFillModeBackwards: The receiver clamps values before zero to zero when the animation is completed.
  • kCAFillModeBoth: receiver 将值固定在对象时间空间的两端。
  • kCAFillModeRemoved: 动画完成后,receiver 将从 presentation 中删除。

CAAction

 允许对象响应由 CALayer 更改触发的 actions 的接口。(仅有一个代理方法的 protocol,CAAnimation 类遵循此协议)

 当使用 action 标识符(key path、外部 action 名称或预定义 action 标识符)查询时,CALayer 返回相应的 action 对象(必须实现 CAAction 协议),并向其发送 runActionForKey:object:arguments: 消息。

Responding to an action(响应操作)

- runActionForKey:object:arguments:

 调用以触发标识符指定的 action。

key: action 的标识符。标识符可以是相对于对象的键或键路径、任意外部 action 或 CALayer 中定义的 action 标识符之一。anObject: 发生 action 的 CALayer。dict: 包含与此 event 关联的参数的字典。可能是 nil。

/** Action (event handler) protocol. **/

@protocol CAAction

- (void)runActionForKey:(NSString *)event object:(id)anObject arguments:(nullable NSDictionary *)dict;

@end

/** NSNull protocol conformance. **/

@interface NSNull (CAActionAdditions) <CAAction>

@end

 /* Called to trigger the event named 'path' on the receiver. *调用以触发 receiver 上名为 'event' 的事件。

  • The object (e.g. the layer) on which the event happened is 'anObject'.
  • 发生事件的对象(例如 CALayer)为 'anObject'。
  • The arguments dictionary may be nil, if non-nil it carries parameters associated with the event.
  • 参数字典可能是 nil,如果非 nil,它携带与事件关联的参数。 */

CALayerDelegate

 CALayer 的 delegate 对象需要遵循此协议,以响应与 CALayer 相关的事件。

@protocol CALayerDelegate <NSObject>
@optional // CALayerDelegate 的协议方法都是可选的
...
@end

 你可以实现此协议的方法来提供 CALayer 的内容、处理 sublayers 的布局以及提供要执行的自定义动画动作(custom animation actions)。必须将实现此协议的对象分配给 CALyer 对象的 delegate 属性。

 在 iOS 中 View 的 layer 的 delegate 默认是 View 本身。如下示例代码打印:

NSLog(@"😻😻 view 本身: %@", self.view);
NSLog(@"😻😻 view 的 layer 的 delegate: %@", self.view.layer.delegate);
// 控制台打印:
😻😻 view 本身: <UIView: 0x7fcdf090b170; frame = (0 0; 390 844); autoresize = W+H; layer = <CALayer: 0x6000038df680>>
😻😻 view 的 layer 的 delegate: <UIView: 0x7fcdf090b170; frame = (0 0; 390 844); autoresize = W+H; layer = <CALayer: 0x6000038df680>>

Providing the Layer's Content(提供 CALayer 的内容)

- displayLayer:

 告诉 delegate 执行显示过程。

- (void)displayLayer:(CALayer *)layer;

layer: 其内容需要更新的 CALayer。

- displayLayer: 委托方法在 CALayer 被标记为要重新加载其内容时被调用,通常由 setNeedsDisplay 方法启动(标记)。典型的更新技术是设置 CALayer 的 contents 属性。

 如果已实现此委托方法,则由 - display 方法的默认实现调用,在这种情况下,它应该实现整个显示过程(通常通过设置 contents 属性)。

- drawLayer:inContext:

 告诉 delegate 使用 CALayer 的 CGContextRef 实现显示过程。

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

layer: 需要绘制其内容的 CALayer。ctx: 用于绘图的图形上下文。图形上下文包含用于绘制到目标屏幕的适当比例因子。

drawLayer:inContext: 委托方法在 CALayer 被标记为要重新加载其内容时调用,通常使用 setNeedsDisplay 方法标记。如果 delegate 实现了 displayLayer: 方法,则不调用它。可以使用上下文来绘制向量,例如曲线和直线,或者使用 draw(_:in:byTiling:) 方法绘制图像。

 Important:如果 delegate 实现了 displayLayer: 方法,则不会调用此方法。

 如果已实现此委托方法,则由 - drawInContext: 方法的默认实现调用。

- layerWillDraw:

 通知 delegate 即将 draw。

- (void)layerWillDraw:(CALayer *)layer API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0));

layer: 将绘制其内容的 CALayer。

 在 drawLayer:inContext: 之前调用 layerWillDraw: 方法。你可以使用此方法在 drawLayer:inContext: 之前配置影响 contents 的任何 CALayer 状态,例如 contentsFormat 和 opaque。

 Important:如果 delegate 实现了 displayLayer: 方法,则不会调用此方法。

 如果已实现此委托方法,则由 - display 方法的默认实现调用。允许委托在 - drawLayer:InContext: 之前配置影响 contents 的任何 CALayer 状态,例如 contentsFormat 和 opaque。如果委托实现 - displayLayer,则不会调用该方法。

Laying Out Sublayers(布局子图层)

- layoutSublayersOfLayer:

 告诉 delegate CALayer 的 bounds 已更改。

- (void)layoutSublayersOfLayer:(CALayer *)layer;

layer: 需要布局其 sublayers 的 CALayer。

layoutSublayersOfLayer: 方法在 CALayer 的 bounds 发生更改时调用,例如通过更改其 frame 的大小。如果需要精确控制 CALayer 的 sublayers 的布局,可以实现此方法。

 在检查 layout manager 之前,由默认的 - layoutSublayers 实现调用。请注意,如果调用了委托方法(- layoutSublayersOfLayer:),则 layout manager 将被忽略。

Providing a Layer's Actions(提供图层的操作)

- actionForLayer:forKey:

 返回 actionForKey: 方法的默认 action。

- (nullable id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event;

layer: 作为 action target 的 CALayer。event: action 的标识符。

 Return Value: 实现 CAAction 协议的对象,如果 delegate 没有为指定的 event 指定行为,则为 nil。

 实现此方法的 CALayer 的 delegate 返回指定键的 action 并停止任何进一步的搜索(即,不返回 CALayer 的 actions 字典中相同键的 action 或 + defaultActionForKey: 指定的 action)。

 如果已实现此委托方法,则由 - actionForKey: 方法的默认实现调用。应返回实现 CAAction 协议的对象。如果 delegate 未指定当前 event 的行为,则可能返回 nil。返回空对象(即 [NSNull null])会明确强制不再进行进一步的搜索。(即,+ defaultActionForKey: 方法将不会被调用。)

CAMediaTiming、CAAction、CALayerDelegate 总结

 到这里看完了 CAMediaTiming、CAAction、CALayerDelegate 三个 protocol。其中 CAAnimation 遵循 CAMediaTiming 和 CAAction 协议,CALayer 遵循 CAMediaTiming 协议,而 CALayerDelegate 则是 CALayer 的 delegate 遵循的协议。

  • CAMediaTiming 协议中控制动画开始时间(beginTime、timeOffset 属性)、设置重复动画的次数或者重复时间(repeatCount、repeatDuration 属性)、设置动画的持续时间和速度(duration、speed 属性)、动画播放模式(autoreverses 是否结束后反向播放、fillMode 动画结束后的 向前、向后、两端、移除)。
  • CAAction 协议则是仅有 CAAnimation 类遵循此协议,仅有一个委托方法 - runActionForKey:object:arguments: 用于响应当 CALayer 更改时触发 Action。(执行添加到 CALayer 上的某个 CAAnimation 动画对象)
  • CALayerDelegate 协议则是提供给 CALayer 的 delegate 必须遵守的协议,实现三个作用:提供 CALayer 的内容、布局 CALayer 子图层(- layoutSublayersOfLayer:)、提供图层的操作(- actionForLayer:forKey:)。 但是它的所有协议方法默认都是可选的(@optional)。其中 - displayLayer:- drawLayer:inContext: 以两种不同的方式为 CALayer 提供内容,不过 - displayLayer: 执行级别高于 - drawLayer:inContext:,当 CALayer 的 delegate 实现了 - displayLayer: 方法后则不再调用 - drawLayer:inContext: 方法。 - displayLayer: 委托方法通常在 CALayer 调用其 setNeedsDisplay 方法标记 CALayer 需要重新加载其内容时被调用,且 CALayer 的 - display 方法的默认实现会调用 - displayLayer: 委托方法。 同样,当 - displayLayer: 委托方法未实现时,- drawLayer:inContext: 委托方法通常在 CALayer 调用其 setNeedsDisplay 方法标记 CALayer 需要重新加载其内容时被调用,不同的是 CALayer 的 - drawInContext: 方法的默认实现会调用 - drawLayer:inContext: 委托方法。 而 - layerWillDraw: 委托方法则是在 - drawLayer:inContext: 之前调用。你可以使用此方法在 - drawLayer:inContext: 之前配置影响 contents 的任何 CALayer 状态,例如 contentsFormat 和 opaque。

 那么下面我们继续详细分析 CALayer 的文档。

CALayer

 管理基于图像的内容并允许你对该内容执行动画的对象。(继承自 NSObject 并遵循 CAMediaTiming 协议)

API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
{
@private
  struct _CALayerIvars {
    int32_t refcount;
    uint32_t magic;
    void *layer;
#if TARGET_OS_MAC && !TARGET_RT_64_BIT
    void * _Nonnull unused1[8];
#endif
  } _attr;
}

Overview(概述)

 Layers 通常用于为 view 提供 backing store,但也可以在没有 view 的情况下使用以显示内容。layer 的主要工作是管理你提供的视觉内容(visual content),但 layer 本身也具有可以设置的视觉属性(visual attributes),例如背景色(background color)、边框(border)和阴影(shadow)。除了管理视觉内容外,layer 还维护有关其内容的几何(geometry)(例如其位置(position)、大小(size)和变换(transform))的信息,这些信息用于在屏幕上显示该内容。修改 layer 的属性是在 layer 的内容或几何(geometry)上启动动画的方式。layer 对象通过 CAMediaTiming 协议封装 layer 及其动画的持续时间(duration)和步调(pacing),该协议定义了 layer 的时间信息(timing information)。

 如果 layer 对象是由 view 创建的,则 view 通常会自动将自身指定为 layer 的 delegate,并且不应更改该关系。对于你自己创建的 layers,可以为其指定一个 delegate 对象,并使用该对象动态提供 layer 的内容并执行其他任务。layer 可能还具有布局管理器(layout manager)对象(指定给 layoutManager 属性),以分别管理子图层(sublayers)的布局。

Creating a Layer(创建 layer)

+ layer

 创建并返回 layer 对象的实例。

+ (instancetype)layer;

 Return Value: 初始化的 layer 对象;如果初始化失败,则返回 nil。

 如果你是 CALayer 的子类,则可以重写此方法,并使用该函数提供特定子类的实例。

- init

 返回一个初始化的 CALayer 对象。

- (instancetype)init;

 这是不在 presentation layer(表示层)中的 layer 对象的指定初始化程序。

- initWithLayer:

 重写以复制或初始化指定 layer 的自定义字段。

 Core Animation 使用此初始值设定项来创建 layers 的 shadow 副本,例如用作 presentation layers。子类可以重写此方法,以将其实例变量复制到 presentation layer 中(子类随后应调用超类)。在任何其他情况下调用此方法都将导致未定义的行为。

- (instancetype)initWithLayer:(id)layer;

layer: 应从其复制自定义字段的 layer。

 Return Value:从 layer 复制的任何自定义实例变量的 layer 实例。

 此初始化程序用于创建 layer 的 shadow 副本,例如,用于 presentationLayer 方法。在任何其他情况下使用此方法都会产生不确定的行为。例如,请勿使用此方法用现有 layer 的内容初始化新 layer。

 如果要实现自定义 layer 子类,则可以重写此方法并将其用于将实例变量的值复制到新对象中。子类应始终调用超类实现。

 此方法是 presentation layer(表示层)中各 layer 对象的指定初始化器。

Accessing Related Layer Objects(访问相关 layer 对象)

- presentationLayer

 返回 presentation layer 对象的副本,该对象表示当前在屏幕上显示的 layer 的状态。

- (nullable instancetype)presentationLayer;

 Return Value: 当前 presentation layer 对象的副本。

 通过此方法返回的 layer 对象提供了当前在屏幕上显示的 layer 的近似值。在动画制作过程中,你可以检索该对象并使用它来获取那些动画的当前值。

 返回 layer 的 sublayers、mask 和 superlayer 属性从表示树(presentation tree)(而不是模型树)返回相应的对象。此模式也适用于任何只读 layer 方法。例如,返回对象的 hitTest: 方法查询 presentation tree 中的 layer 对象。

 返回包含所有属性的 CALayer 的副本,这些属性与当前 transaction 开始时的属性相同,并应用了所有活动动画。这非常接近当前显示的 CALayer 版本。如果尚未 committed 该 CALayer,则返回 nil。  尝试以任何方式修改返回的 CALayer 对象,其产生的效果都是不确定的。(返回的 CALayer 对象仅应当用于读取其当前的各种属性)  返回的 CALayer 对象的 sublayers、mask 和 superlayer 属性返回的是这些属性的 presentation versions 。这将执行只读 CALayer 方法。例如,在 - presentationLayer 返回的 CALayer 对象上调用 - hitTest: 将查询 layer tree 的 presentation values。

- modelLayer

 返回与 receiver 关联的 model layer 对象(如果有)。

- (instancetype)modelLayer;

 Return Value: 表示基础模型层(underlying model layer)的 CALayer 实例。

 在 presentation tree 中的 CALayer 上调用此方法将返回 model tree 中的相应 CALayer 对象。仅当涉及表示层更改的事务正在进行时,此方法才返回值。如果没有正在进行的事务,则调用此方法的结果是不确定的。

 在 - presentationLayer 方法的结果上调用时,返回具有当前模型值的基础层。在非表示层上调用时,返回接收者。产生表示层的事务完成后调用此方法的结果是不确定的。

 上面 - presentationLayer- modelLayer 函数涉及到的 “表示树” 和 “模型树” 在后面的 Core Animation 文档中有详细解释。

Accessing the Delegate(访问 CALayer 的代理)

delegate

 CALayer 的委托对象。(delegate 是遵循 CALayerDelegate 协议的 weak 属性)

@property(nullable, weak) id <CALayerDelegate> delegate;

 你可以使用委托对象来提供图层的内容,处理任何子图层的布局以及提供自定义操作以响应与图层相关的更改。你分配给此属性的对象应实现 CALayerDelegate 非正式协议的一种或多种方法。关于协议的更多信息,请参见上面 CALayerDelegate 协议分析。

 在 iOS 中,如果图层与 UIView 对象关联,则必须将此属性设置为拥有该 CALayer 的 UIView。(在 iOS 中 UIView 默认作为其 layer 属性的 delegate 对象,但是对于自己创建的 CALayer 对象其 delegate 属性默认是 nil)

Providing the Layer’s Content(提供 CALayer 的内容)

contents

 提供 CALayer 内容的对象。可动画的。(strong 修饰的 id 类型的属性)

@property(nullable, strong) id contents;

 此属性的默认值为 nil。

 如果使用 CALayer 显示静态图像,则可以将此属性设置为 CGImageRef,其中包含要显示的图像。 (在 macOS 10.6 及更高版本中,你也可以将此属性设置为 NSImage 对象。)为该属性分配值会导致 CALayer 使用你的图像,而不是创建单独的 backing store。

 如果图层对象绑定到 UIView 对象,则应避免直接设置此属性的内容。视图和图层之间的相互作用通常会导致视图在后续更新期间替换此属性的内容。

contentsRect

 单位坐标空间中的矩形,用于定义应使用的 CALayer 内容部分。可动画的。

@property CGRect contentsRect;

 默认为单位矩形(0.0、0.0、1.0、1.0)。

 如果请求单位矩形之外的像素,则内容图像的边缘像素将向外扩展。

 如果提供了一个空矩形,则结果是不确定的。

 标准化图像坐标中的矩形,定义了将被绘制到图层中的 contents 属性的子矩形。如果请求单位矩形之外的像素,则内容图像的边缘像素将向外扩展。如果提供了一个空矩形,则结果是不确定的。默认为单位矩形[0 0 1 1]。可动画的。

contentsCenter

 用于定义在调整图层内容大小时如何缩放图层内容。可动画的。

@property CGRect contentsCenter;

 可以使用此属性将图层内容细分为 3x3 网格。此属性中的值指定网格中中心矩形的位置和大小。如果层的 contentsGravity 属性设置为某个调整大小模式,则调整层的大小会导致网格的每个矩形中发生不同的缩放。中心矩形在两个维度上都拉伸,上中心和下中心矩形仅水平拉伸,左中心和右中心矩形仅垂直拉伸,四角矩形完全不拉伸。因此,你可以使用此技术使用三部分或九部分图像来实现可拉伸的背景或图像。

 默认情况下,此属性中的值设置为单位矩形(0.0,0.0)(1.0,1.0),这将导致整个图像在两个维度上缩放。如果指定的矩形超出单位矩形,则结果未定义。只有在将 contentsRect 属性应用于图像之后,才应用指定的矩形。

 Note: 如果此属性中矩形的宽度或高度很小或为 0,则该值将隐式更改为以指定位置为中心的单个源像素的宽度或高度。

 标准化图像坐标中的矩形定义了 contents 图像的缩放中心部分。  当图像由于其 contentsGravity 属性而调整大小时,其中心部分隐式定义了 3x3 网格,该网格控制如何将图像缩放到其绘制的大小。中心部分在两个方向上都拉伸。顶部和底部仅水平拉伸;左右部分仅垂直拉伸;四个角部分根本没有拉伸。 (这通常称为 "9-slice scaling"。)  矩形在应用了 contentsRect 属性的效果后被解释。默认为单位矩形 [0 0 1 1],表示整个图像都会缩放。作为特殊情况,如果宽度或高度为零,则将其隐式调整为以该位置为中心的单个源像素的宽度或高度。如果矩形延伸到 [0 0 1 1] 单元矩形的外部,则结果不确定。可动画的。

- display

 重新加载该层的内容。

- (void)display;

 不要直接调用此方法。CALayer 会在适当的时候调用此方法以更新 CALayer 的内容。如果 CALayer 具有 delegate 对象,则此方法尝试调用 delegate 的 displayLayer: 方法,delegate 可使用该方法来更新 CALayer 的内容。如果 delegate 未实现 displayLayer: 方法,则此方法将创建 backing store 并调用 CALayer 的 drawInContext: 方法以将内容填充到该 backing store 中。新的 backing store 将替换该 CALayer 的先前内容。

 子类可以重写此方法,并使用它直接设置 CALayer 的 contents 属性。如果你的自定义 CALayer 子类对图层更新的处理方式不同,则可以执行此操作。

 重新加载 CALayer 的内容。调用 - drawInContext: 方法,然后更新 CALayer 的 contents 属性。通常,不直接调用它。

- drawInContext:

 使用指定的图形上下文绘制 CALayer 的内容。

- (void)drawInContext:(CGContextRef)ctx;

ctx: 在其中绘制内容的图形上下文。上下文可以被裁剪以保护有效的 CALayer 内容。希望找到要绘制的实际区域的子类可以调用 CGContextGetClipBoundingBox。

 此方法的默认实现本身不会进行任何绘制。如果 CALayer 的 delegate 实现了 - drawLayer:inContext: 方法,则会调用该方法进行实际绘制。

 子类可以重写此方法,并使用它来绘制 CALayer 的内容。绘制时,应在逻辑坐标空间中的点指定所有坐标。

 当 contents 属性被更新时,通过 - display 方法调用。默认实现不执行任何操作。上下文可以被裁剪以保护有效的 CALayer 内容。希望找到要绘制的实际区域的子类可以调用 CGContextGetClipBoundingBox()

Modifying the Layer’s Appearance(修改 CALayer 的外观)

contentsGravity

 一个常数,指定 CALayer 的 contents 如何在其 bounds 内 positioned 或 scaled。

@property(copy) CALayerContentsGravity contentsGravity;

 Contents Gravity Values 中列出了此属性的可能值。

 此属性的默认值为 kCAGravityResize。(调整内容大小以适合整个 bounds 矩形,可能会被拉伸变形)

 Important: 内容重力常数(contents gravity constants)的命名基于垂直轴的方向。如果将重力常数与垂直分量(例如 kCAGravityTop)一起使用,则还应检查层的 contentsAreFlipped。如果该选项为 YES,kCAGravityTop 将 contents 与 CALayer 的底部对齐,kCAGravityBottom 将内容与层的顶部对齐。

 macOS 和 iOS 中视图的默认坐标系在垂直轴的方向上不同:在 macOS 中,默认坐标系的原点位于绘图区域的左下角,正值从中向上延伸,在 iOS 中,默认坐标系的原点位于绘图区域的左上角,正值从该坐标系向下延伸。

 图1显示了四个示例,这些示例为图层的 contentsGravity 属性设置不同的值。

 Figure 1 Different effects of setting a layer's contents gravity

layer_contents_gravity

  1. Contents gravity is kCAGravityResize - the default
  2. Contents gravity is kCAGravityCenter
  3. Contents gravity is contentsAreFlipped ? kCAGravityTop : kCAGravityBottom
  4. Contents gravity is contentsAreFlipped ? kCAGravityBottomLeft : kCAGravityTopLeft

 一个字符串,定义了如何将 CALayer 的 contents 映射到其 bounds 矩形。选项为'center','top','bottom','left','right','topLeft','topRight','bottomLeft','bottomRight','resize','resizeAspect','resizeAspectFill'。默认值为`resize'。注意,'bottom' 始终表示 最小 Y,'top' 始终表示 最大 Y

Contents Gravity Values

 当 CALayer bounds 大于内容对象的 bounds 时,内容重力常量指定内容对象的位置。它们由 contentsGravity 属性使用。

CA_EXTERN CALayerContentsGravity const kCAGravityCenter API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityTop API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityBottom API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityLeft API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityRight API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityTopLeft API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityTopRight API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityBottomLeft API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityBottomRight API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityResize API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityResizeAspect API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CALayerContentsGravity const kCAGravityResizeAspectFill API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
  • kCAGravityCenter: 内容在 bounds 矩形中水平和垂直居中。
  • kCAGravityTop: 内容在 bounds 矩形的上边缘水平居中。
  • kCAGravityBottom: 内容在 bounds 矩形的下边缘水平居中。
  • kCAGravityLeft: 内容在 bounds 矩形的左边缘垂直居中。
  • kCAGravityRight: 内容在 bounds 矩形的右边缘垂直居中。
  • kCAGravityTopLeft: 内容位于 bounds 矩形的左上角。
  • kCAGravityTopRight: 内容位于 bounds 矩形的右上角。
  • kCAGravityBottomLeft: 内容位于 bounds 矩形的左下角。
  • kCAGravityBottomRight: 内容位于 bounds 矩形的右下角。
  • kCAGravityResize: 调整内容大小以适合整个 bounds 矩形。(可能会拉伸变形)
  • kCAGravityResizeAspect: 调整内容大小以适合 bounds 矩形,从而保留内容的外观(保留宽高比例)。如果内容没有完全填充 bounds 矩形,则内容将以部分轴为中心。
  • kCAGravityResizeAspectFill:调整内容大小以完全填充 bounds 矩形,同时仍保留内容的外观(保留宽高比例)。内容以其超过的轴为中心。

opacity

 receiver 的不透明度。可动画的。

@property float opacity;

 此属性的值必须在 0.0(透明)到 1.0(不透明)之间。超出该范围的值将被限制为最小值或最大值。此属性的默认值为1.0。

 CALayer 的不透明度,介于 0 和 1 之间的值。默认为 1。指定超出 [0,1] 范围的值将产生不确定的结果。可动画的。

hidden

 指示是否隐藏 CALayer 的布尔值。可动画的。

@property(getter=isHidden) BOOL hidden;

 此属性的默认值为 NO。

 如果为true,则不显示该图层及其子图层。默认为 NO。可动画的。

masksToBounds

 一个布尔值,指示是否将 sublayers 裁剪到该 CALayer 的 bounds。可动画的。

@property BOOL masksToBounds;

 当此属性的值为 YES 时,Core Animation 将创建一个隐含的剪贴 mask,该 mask 与 CALayer 的 bounds 匹配并包括任何 corner radius 效果。如果还指定了 mask 属性的值,则将两个 masks 相乘以获得最终的 mask 值。

 此属性的默认值为 NO。

mask

 可选图层,其 Alpha 通道用于屏蔽 CALaer 的内容。

@property(nullable, strong) __kindof CALayer *mask;

 CALayer 的 Alpha 通道决定了该 CALayer 的内容和背景可以显示多少。完全或部分不透明的像素允许基础内容显示出来,但是完全透明的像素会阻止该内容。

 此属性的默认值为 nil。配置 mask 时,请记住设置 mask 层的大小和位置,以确保其与 mask 的 CALayer 正确对齐。

 你分配给此属性的 CALayer 不得具有 superlayer。如果是这样,则行为是不确定的。

 一个 CALayer,其 alpha 通道用作 mask,以在 CALayer 的背景和将其内容与其过滤的背景合成的结果之间进行选择。默认为 nil。当用作 mask 时,图层的 compositingFilter 和 backgroundFilters 属性将被忽略。将 mask 设置为 new layer 时,其 superlayer 必须为 nil,否则行为是不确定的。不支持嵌套 masks(具有自己的 mask 的 mask layer)。

doubleSided

 一个布尔值,指示当 facing away from the viewer,CALayer 是否显示其内容。可动画的。

@property(getter=isDoubleSided) BOOL doubleSided;

 当此属性的值为 NO 时,该层将背对查看器时隐藏其内容(when it faces away from the viewer)。此属性的默认值为 YES。

cornerRadius

 为 CAlayer 的背景绘制圆角时要使用的半径。可动画的。

@property CGFloat cornerRadius;

 将 radius 设置为大于 0.0 时 CALayer 可开始在其背景上绘制圆角。默认情况下,corner radius 不应用于 CALayer contents 属性中的图像;它仅应用于 CALayer 的背景色和边框。但是,将 masksToBounds 属性设置为 YES 会将内容剪裁到圆角。

 此属性的默认值为 0.0。

maskedCorners

 定义使用 cornerRadius 属性时四个角中的哪个角接受遮罩。默认为所有四个角。

@property CACornerMask maskedCorners API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0));

CACornerMask

 maskedCorners 属性的位定义。

typedef NS_OPTIONS (NSUInteger, CACornerMask)
{
  kCALayerMinXMinYCorner = 1U << 0,
  kCALayerMaxXMinYCorner = 1U << 1,
  kCALayerMinXMaxYCorner = 1U << 2,
  kCALayerMaxXMaxYCorner = 1U << 3,
};

borderWidth

 CALayer 边框的宽度。可动画的。

@property CGFloat borderWidth;

 当此值大于 0.0 时,CALayer 将使用当前的 borderColor 值绘制边框。边框是根据此属性中指定的值从接收者的 bounds 绘制的。它在接收者的内容和子层之上进行了合成,并包含 cornerRadius 属性的效果。

 此属性的默认值为 0.0。

borderColor

 CALayer 边框的颜色。可动画的。(类型是 CGColorRef)

@property(nullable) CGColorRef borderColor;

 此属性的默认值为不透明的黑色。

 使用 Core Foundation retain/release 语义保留此属性的值。尽管该属性声明似乎使用默认的 assign 语义进行对象保留,但仍会发生此行为。

backgroundColor

 CALayer 的背景色。可动画的。

@property(nullable) CGColorRef backgroundColor;

 此属性的默认值为 nil。

 使用 Core Foundation retain/release 语义保留此属性的值。尽管该属性声明似乎使用默认的 assign 语义进行对象保留,但仍会发生此行为。

shadowOpacity

 CALayer 阴影的不透明度。可动画的。

@property float shadowOpacity;

 此属性中的值必须在 0.0(透明)到 1.0(不透明)之间。此属性的默认值为 0.0。

 阴影的不透明度。默认值为 0。指定 [0,1] 范围以外的值将得到不确定的结果。可动画的。

shadowRadius

 用于渲染 CALayer 阴影的模糊半径(以 points 为单位)。可动画的。

@property CGFloat shadowRadius;

 指定半径此属性的默认值为 3.0。

 用于创建阴影的模糊半径。默认值为 3.0。可设置动画。

shadowOffset

 CALayer 阴影的偏移量(以 points 为单位)。可动画的。

@property CGSize shadowOffset;

 此属性的默认值为(0.0,-3.0)。

shadowColor

 CALayer 阴影的颜色。可动画的。

@property(nullable) CGColorRef shadowColor;

 使用 Core Foundation retain/release 语义保留此属性的值。尽管该属性声明似乎使用默认的 assign 语义进行对象保留,但仍会发生此行为。

 阴影的颜色。默认为不透明黑色。当前不支持从图案创建的颜色。可动画的。

shadowPath

 CALayer 阴影的形状。可动画的。

@property(nullable) CGPathRef shadowPath;

 此属性的默认值为 nil,这会导致 CALayer 使用标准阴影形状(tandard shadow shape)。如果为此属性指定值,则 CALayer 将使用指定的路径而不是 CALayer 的合成 alpha 通道创建其阴影。你提供的路径定义了阴影的轮廓。使用 non-zero winding 规则和当前阴影颜色、不透明度和模糊半径填充。

 与大多数可设置动画的属性不同,此属性(与所有 CGPathRef 可设置动画的属性一样)不支持隐式动画。但是,可以使用 CAPropertyAnimation 的任何具体子类为路径对象设置动画。路径将插值为 "on-line" 点的线性混合;"off-line" 点可以非线性插值(以保持曲线导数的连续性)。如果两条路径具有不同数量的控制点或段,则结果未定义。如果路径延伸到层边界之外,它将不会自动剪裁到层,只有在正常的 masking rules 规则导致这种情况时。

 指定显式路径通常可以提高渲染性能。

 使用 Core Foundation retain/release 语义保留此属性的值。尽管该属性声明似乎使用默认的 assign 语义进行对象保留,但仍会发生此行为。

 当为非 null 时,此路径定义用于构造图层阴影的轮廓,而不是使用图层的合成 Alpha 通道。使用非零缠绕规则渲染路径。使用此属性显式指定路径通常可以提高渲染性能,因为可以在多个图层之间共享相同的路径引用。分配后,路径将被复制。默认为 nil。可动画的。

Using Shadow Path for Special Effects(使用阴影路径进行特殊效果)

 你可以使用图层的阴影路径来创建特殊效果,例如模拟 Pages 中可用的阴影。

 清单1 显示了将椭圆阴影添加到图层底部以模拟 Pages Contact Shadow 效果所需的代码。

 Listing 1 Creating a contact shadow path

let layer = CALayer()
     
layer.frame = CGRect(x: 75, y: 75, width: 150, height: 150)
layer.backgroundColor = NSColor.darkGray.cgColor
layer.shadowColor = NSColor.gray.cgColor
layer.shadowRadius = 5
layer.shadowOpacity = 1
     
let contactShadowSize: CGFloat = 20
let shadowPath = CGPath(ellipseIn: CGRect(x: -contactShadowSize,
                                          y: -contactShadowSize * 0.5,
                                          width: layer.bounds.width + contactShadowSize * 2,
                                          height: contactShadowSize),
                        transform: nil)
     
layer.shadowPath = shadowPath

 Figure 1 Layer with contact shadow effect

Layer_with_contact_shadow_effect

 清单 2 显示了如何创建路径来模拟 Pages Curved Shadow。路径的左侧,顶部和右侧是直线,而底部是凹曲线,如图 2 所示。

 Figure 2 Shadow path for curved shadow effect

Shadow_path_for_curved_shadow_effect

 Listing 2 Creating a curved shadow path

let layer = CALayer()
layer.frame = CGRect(x: 75, y: 75, width: 150, height: 150)
layer.backgroundColor = NSColor.darkGray.cgColor
     
layer.shadowColor = NSColor.black.cgColor
layer.shadowRadius = 5
layer.shadowOpacity = 1
     
let shadowHeight: CGFloat = 10
let shadowPath = CGMutablePath()
shadowPath.move(to: CGPoint(x: layer.shadowRadius,
                            y: -shadowHeight))
shadowPath.addLine(to: CGPoint(x: layer.shadowRadius,
                               y: shadowHeight))
shadowPath.addLine(to: CGPoint(x: layer.bounds.width - layer.shadowRadius,
                               y: shadowHeight))
shadowPath.addLine(to: CGPoint(x: layer.bounds.width - layer.shadowRadius,
                               y: -shadowHeight))
     
shadowPath.addQuadCurve(to: CGPoint(x: layer.shadowRadius,
                                    y: -shadowHeight),
                        control: CGPoint(x: layer.bounds.width / 2,
                                         y: shadowHeight))
     
layer.shadowPath = shadowPath

 Figure 3 Layer with curved shadow effect

Layer_with_curved_shadow_effect

style

 可选 NSDictionary,用于存储未由 CALayer 明确定义的属性值

@property(nullable, copy) NSDictionary *style;

 该 NSDictionary 又可以具有 style key,从而形成默认值的层次结构。如果是分层样式字典(hierarchical style dictionaries),则使用属性的最浅值。例如,style.someValue 的值优先于 style.style.someValue。

 如果 style dictionary 未为属性定义值,则调用 CALayer 的 + defaultValueForKey: 方法。此属性的默认值为 nil。

 下列关键字不参考 style dictionary:bounds、frame。

 Warning: 如果修改了 style dictionary 或其任何 ancestors,则在重置样式属性之前,CALayer 属性的值是不确定的。

 非 nil 时,dictionary 取消引用以查找 CALayer 未显式定义的属性值。 (此 dictionary 可能又具有 style 属性,形成默认值的层次结构。)如果 style dictionary 未为属性定义值,则调用 + defaultValueForKey: 方法。默认为 nil。

allowsEdgeAntialiasing

 一个布尔值,指示是否允许该 CALayer 执行边缘抗锯齿。

@property BOOL allowsEdgeAntialiasing API_AVAILABLE(macos(10.10), ios(2.0), watchos(2.0), tvos(9.0));

 值为 YES 时,允许 CALayer 按照其 edgeAntialiasingMask 属性中的值要求对其边缘进行抗锯齿。默认值是从 main bundle 的 Info.plist 文件中的 boolean UIViewEdgeAntialiasing 属性读取的。如果未找到任何值,则默认值为 NO。

allowsGroupOpacity

 一个布尔值,指示是否允许该 CALayer 将自身与其父级分开组合为一个组。

@property BOOL allowsGroupOpacity API_AVAILABLE(macos(10.10), ios(2.0), watchos(2.0), tvos(9.0));

 当值为 YES 且 CALayer 的 opacity 属性值小于 1.0 时,允许 CALayer 将其自身组合为与其父级分开的组。当 CALayer 包含多个不透明组件时,这会给出正确的结果,但可能会降低性能。

 默认值是从 main bundle 的 Info.plist 文件中的 boolean UIViewGroupOpacity 属性读取的。如果未找到任何值,则对与 iOS 7 SDK 或更高版本链接的应用程序的默认值为 YES,对于与较早的 SDK 链接的应用程序的默认值为 NO。

Layer Filters(图层过滤器)

filters

 一组 Core Image 过滤器,可应用于 CALayer 及其 sublayers 的内容。可动画的。

@property(nullable, copy) NSArray *filters;

 你添加到此属性的过滤器会影响 CALayer 的内容,包括其 border,填充的背景和 sublayers。此属性的默认值为 nil。

 在 CIFilter 对象附加到 CALayer 之后直接更改其输入会导致未定义的行为。可以在将过滤器附着到 CALayer 后修改过滤器参数,但必须使用图层的 setValue:forKeyPath: 方法执行此操作。此外,必须为过滤器指定一个名称,以便在数组中标识它。例如,要更改过滤器的 inputRadius 参数,可以使用类似以下代码:

CIFilter *filter = ...;
CALayer *layer = ...;
 
filter.name = @"myFilter";
layer.filters = [NSArray arrayWithObject:filter];
[layer setValue:[NSNumber numberWithInt:1] forKeyPath:@"filters.myFilter.inputRadius"];

 iOS 中的图层不支持此属性。

compositingFilter

 一个 Core Image 滤镜,用于合成 CALayer 及其背后的内容。可动画的。

@property(nullable, strong) id compositingFilter;

 此属性的默认值为 nil,这将导致图层使用源覆盖合成。尽管你可以将任何 Core Image 滤镜用作图层的合成滤镜,但为获得最佳效果,请使用 CICategoryCompositeOperation 类别中的滤镜。

 在 macOS 中,可以在将过滤器附加到图层后再对其参数进行修改,但是你必须使用该图层的 setValue:forKeyPath: 方法。例如,要更改过滤器的 inputRadius 参数,可以使用类似于以下代码:

CIFilter *filter = ...;
CALayer *layer = ...;
 
layer.compositingFilter = filter;
[layer setValue:[NSNumber numberWithInt:1] forKeyPath:@"compositingFilter.inputRadius"];

 在 CIFilter 对象附加到层之后直接更改其输入会导致未定义的行为。

 iOS 中的图层不支持此属性。

backgroundFilters

 一组 Core Image 过滤器,可应用于紧靠该图层后面的内容。可动画的。

@property(nullable, copy) NSArray *backgroundFilters;

 背景过滤器会影响显示在图层本身中的图层后面的内容。通常,此内容属于充当该层父级的 superlayer。这些滤镜不会影响图层本身的内容,包括图层的背景颜色和边框。

 此属性的默认值为 nil。

 在 CIFilter 对象附加到层之后直接更改其输入会导致未定义的行为。在 macOS 中,可以在将过滤器附加到图层后修改过滤器参数,但必须使用图层的 setValue:forKeyPath: 执行此操作的方法。此外,必须为过滤器指定一个名称,以便在数组中标识它。例如,要更改过滤器的 inputRadius 参数,可以使用类似以下代码:

CIFilter *filter = ...;
CALayer *layer = ...;
 
filter.name = @"myFilter";
layer.backgroundFilters = [NSArray arrayWithObject:filter];
[layer setValue:[NSNumber numberWithInt:1] forKeyPath:@"backgroundFilters.myFilter.inputRadius"];

 你可以使用图层的 masksToBounds 来控制其背景滤镜效果的程度。

 应用于图层背景的滤镜数组。root layer 将忽略此属性。可动画的。

minificationFilter

 减小内容大小时使用的过滤器。

@property(copy) CALayerContentsFilter minificationFilter;

 Scaling Filters 中列出了此属性的可能值。

 此属性的默认值为 kCAFilterLinear。

 呈现 CALayer 的 contents 属性时要使用的过滤器类型。缩小滤镜用于减小图像数据的大小,放大滤镜用于增大图像数据的大小。当前允许的值为 "nearest" 和 "linear"。这两个属性默认为 "linear"。

minificationFilterBias

 缩小过滤器用来确定详细程度的偏差因子。

@property float minificationFilterBias;

 当将此值设置为 kCAFilterTrilinear 时,minificationFilter 将使用此值。

 此属性的默认值为 0.0。

 在确定使用三线性过滤最小化时使用的细节级别时添加的偏差因子。默认值为 0。可设置动画。

magnificationFilter

 增加内容大小时使用的过滤器。

@property(copy) CALayerContentsFilter magnificationFilter;

 Scaling Filters 中列出了此属性的可能值。

 此属性的默认值为 kCAFilterLinear。

 图 1 显示了当将一个10 x 10 点的圆图像放大 10 倍时,线性 filtering 和最近 filtering 之间的差异。

 Figure 1 Circle with different magnification filters

Circle_with_different_magnification_filters

 左侧的圆圈使用 kCAFilterLinear,右侧的圆圈使用 kCAFilterNearest。