UIView/CALayer 如何在没有 drawRect: 的情况下呈现自身?
How does a UIView/CALayer render itself without drawRect:?
Apple 的 Core Animation 文档中说涉及两个渲染路径。据我所知,CALayer 缓存了 UIView
内容的位图数据。有两种方式提供 CALayer 的内容。一种是实现 drawRect:
或其他 CALayer 绘制方法,另一种是将位图设置为 CALayer 的 属性 内容。
这里我想知道,如果以上两件事都不做,幕后会发生什么?我相信在这种情况下会使用私人绘图路径 UIView
。这个私人绘图路径由什么组成?它是如何工作的?
可以通过三种方式向图层提供内容。
- Assign an image object directly to the layer object’s contents property.
- Assign a delegate object to the layer and let the delegate draw the layer’s content.
- Define a layer subclass and override one of its drawing methods to provide the layer contents yourself.
如果我们不实现 drawRect:
或设置 contents
属性 或子类化层,它将使用默认方式,即第二种方式向 Layer-backed 视图的层提供内容.该层将捕获视图的内容并进行渲染。
CALayer 不使用 drawRect: 绘制它的任何内容。只有像 Core Graphics 这样的基于视图的绘图技术使用 drawRect: 但这种方式的唯一问题是它是在 CPU 和主线程上完成的,这使得它成为一个昂贵的过程。因此,Core Animation 直接在图形硬件中操作应用程序内容的缓存位图,这要优化得多。您可以通过其委托之一(displayLayer: 或 drawLayer:inContext)或使用您提到的内容 属性 更新或提供核心动画层的初始内容。 Core Animation 中的所有层对象都派生自 CALayer。
CALayer 只是属于Core Animation 的层对象,它本身只是UIView 或UIView 子类的支持系统。 Core Animation 不是一种绘图技术,因为它不能像 Quartz、Open GL ES 和 Metal 那样创建原始形状。相反,Core Animation 允许您操作一个已经存在的视图,它通过缓存 UIView 的位图数据并将其发送到要操作的图形硬件来实现。我们说 Core Animation 是一个支持系统,它的所有工作都依赖于使用图层对象,其中 CALayer 是主要类型。它当然只能在视图有层并且视图实际上不需要层存在的情况下才能这样做。但是,在 iOS 中,所有视图都默认附加一个图层。我们说 iOS 中的视图是 "layer backed"。在 MacOS 中,您实际上需要为视图添加 Core Animation 支持。
CALayer 内容的实际绘制有几种方式。第一个是如您所提到的那样更改 CALayer 的内容 属性,您可以通过给它一个 CGImageRef 来做到这一点。第二种是通过在子类中实现或覆盖 CALayer 委托 displayLayer: 创建位图并将其设置为层的内容 属性。第三种是通过在子类中实现或重写 CALayer 委托 drawLayer:inContext,它创建一个位图,创建一个图形上下文以绘制到该位图中,然后调用您的委托方法来填充位图。
在 iOS 中,我们通常不会担心视图层的内容是如何呈现的。由于所有视图都是图层支持的,iOS 管理如何使用我刚才描述的方法以最有效的方式呈现这些视图。这是一种节省时间的优化,它使层非常易于使用。如果您正在为 MacOS 开发视图并不总是支持层的,您通常会担心覆盖这些委托或子类化它们。如果您决定不使用 iOS 中的默认 CALayer,您可能也会担心这一点,例如,您可能会将视图的图层从 CALayer 更改为 CAMetalLayer。或者,如果您正在寻找性能优化,甚至在少数情况下也是如此。
CALayer 的症结在于它由 GPU 支持。在现代图形和动画中,您希望尽量减少位图数据在 CPU 和 GPU 之间传输的次数。这些操作成本很高。
CALayer 总是使用私有绘图路径,无论你使用setContents:
还是drawRect:
。事实上,CALayer 的底层管道以基本相同的方式处理这两种情况。当你 setContents:
时,CALayer 获取你给它的图像,并通过 OpenGL(现在可能是 Metal)调用将它上传到 GPU。当你 drawRect:
CALayer 给你一个你可以在其中绘图的上下文时,然后它对位图数据做同样的事情。
如果你不设置内容或实现drawRect,你仍然可以设置图层的背景颜色、边框、圆角半径等。这是由CALayer基于GPU的私有绘图路径渲染的。
Apple 的 Core Animation 文档中说涉及两个渲染路径。据我所知,CALayer 缓存了 UIView
内容的位图数据。有两种方式提供 CALayer 的内容。一种是实现 drawRect:
或其他 CALayer 绘制方法,另一种是将位图设置为 CALayer 的 属性 内容。
这里我想知道,如果以上两件事都不做,幕后会发生什么?我相信在这种情况下会使用私人绘图路径 UIView
。这个私人绘图路径由什么组成?它是如何工作的?
可以通过三种方式向图层提供内容。
- Assign an image object directly to the layer object’s contents property.
- Assign a delegate object to the layer and let the delegate draw the layer’s content.
- Define a layer subclass and override one of its drawing methods to provide the layer contents yourself.
如果我们不实现 drawRect:
或设置 contents
属性 或子类化层,它将使用默认方式,即第二种方式向 Layer-backed 视图的层提供内容.该层将捕获视图的内容并进行渲染。
CALayer 不使用 drawRect: 绘制它的任何内容。只有像 Core Graphics 这样的基于视图的绘图技术使用 drawRect: 但这种方式的唯一问题是它是在 CPU 和主线程上完成的,这使得它成为一个昂贵的过程。因此,Core Animation 直接在图形硬件中操作应用程序内容的缓存位图,这要优化得多。您可以通过其委托之一(displayLayer: 或 drawLayer:inContext)或使用您提到的内容 属性 更新或提供核心动画层的初始内容。 Core Animation 中的所有层对象都派生自 CALayer。
CALayer 只是属于Core Animation 的层对象,它本身只是UIView 或UIView 子类的支持系统。 Core Animation 不是一种绘图技术,因为它不能像 Quartz、Open GL ES 和 Metal 那样创建原始形状。相反,Core Animation 允许您操作一个已经存在的视图,它通过缓存 UIView 的位图数据并将其发送到要操作的图形硬件来实现。我们说 Core Animation 是一个支持系统,它的所有工作都依赖于使用图层对象,其中 CALayer 是主要类型。它当然只能在视图有层并且视图实际上不需要层存在的情况下才能这样做。但是,在 iOS 中,所有视图都默认附加一个图层。我们说 iOS 中的视图是 "layer backed"。在 MacOS 中,您实际上需要为视图添加 Core Animation 支持。
CALayer 内容的实际绘制有几种方式。第一个是如您所提到的那样更改 CALayer 的内容 属性,您可以通过给它一个 CGImageRef 来做到这一点。第二种是通过在子类中实现或覆盖 CALayer 委托 displayLayer: 创建位图并将其设置为层的内容 属性。第三种是通过在子类中实现或重写 CALayer 委托 drawLayer:inContext,它创建一个位图,创建一个图形上下文以绘制到该位图中,然后调用您的委托方法来填充位图。
在 iOS 中,我们通常不会担心视图层的内容是如何呈现的。由于所有视图都是图层支持的,iOS 管理如何使用我刚才描述的方法以最有效的方式呈现这些视图。这是一种节省时间的优化,它使层非常易于使用。如果您正在为 MacOS 开发视图并不总是支持层的,您通常会担心覆盖这些委托或子类化它们。如果您决定不使用 iOS 中的默认 CALayer,您可能也会担心这一点,例如,您可能会将视图的图层从 CALayer 更改为 CAMetalLayer。或者,如果您正在寻找性能优化,甚至在少数情况下也是如此。
CALayer 的症结在于它由 GPU 支持。在现代图形和动画中,您希望尽量减少位图数据在 CPU 和 GPU 之间传输的次数。这些操作成本很高。
CALayer 总是使用私有绘图路径,无论你使用setContents:
还是drawRect:
。事实上,CALayer 的底层管道以基本相同的方式处理这两种情况。当你 setContents:
时,CALayer 获取你给它的图像,并通过 OpenGL(现在可能是 Metal)调用将它上传到 GPU。当你 drawRect:
CALayer 给你一个你可以在其中绘图的上下文时,然后它对位图数据做同样的事情。
如果你不设置内容或实现drawRect,你仍然可以设置图层的背景颜色、边框、圆角半径等。这是由CALayer基于GPU的私有绘图路径渲染的。