当您覆盖makeBackingLayer 时,您有责任设置该层,包括设置其delegate: CALayerDelegate。使用该层将delegate 设置为视图就足够了。从那里,您可以实现任何这些委托方法,尽管您可能想要func display(_ layer: CALayer)。有关更多信息,请参阅Core Animation Guide - Providing a Layer's Contents。
updateLayer 的代码路径取决于使用默认层类型的NSView。这是我正在工作的NSView 子类,它在边界更改时重绘:
/**
A view backed by a CAShapeLayer that can inscribe an image inside the
shape layer's circular path
*/
@IBDesignable final class InscribedImageView: NSView, CALayerDelegate {
@IBInspectable private var image: NSImage?
@IBInspectable private var color: NSColor = .clear
// swiftlint:disable:next force_cast
private var shapeLayer: CAShapeLayer { return self.layer as! CAShapeLayer }
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
configureViewSetting()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
configureViewSetting()
}
private func configureViewSetting() {
wantsLayer = true
}
override func makeBackingLayer() -> CALayer {
let layer = CAShapeLayer()
layer.delegate = self
layer.needsDisplayOnBoundsChange = true
return layer
}
func display(_ layer: CALayer) {
inscribe(image, in: color)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
inscribe(image, in: color)
}
func inscribe(_ image: NSImage?, in color: NSColor) {
self.color = color
defineCircle(color: color)
subviews.forEach { $0.removeFromSuperview() }
if let image = image {
self.image = image
let imageView = NSImageView(image: image)
imageView.imageScaling = .scaleProportionallyUpOrDown
imageView.frame = bounds
addSubview(imageView)
}
}
private func defineCircle(color: NSColor) {
shapeLayer.path = CGPath(ellipseIn: shapeLayer.bounds, transform: nil)
shapeLayer.fillColor = color.cgColor
shapeLayer.strokeColor = color.cgColor
}
}
请注意,我不需要设置 wantsUpdateLayer 以在我的子类或子类的 layerContentsRedrawPolicy = LayerContentsRedrawPolicy.duringViewResize 上返回 true。这大概是因为两者都依赖于股票层类型,而不是子类。
以下是我之前的错误答案中的一些更有用的资源:
Apple's Core Animation Guide
重写 WantUpdateLayer 并返回 YES 会导致 NSView 类遵循备用渲染路径。视图调用您的 updateLayer 方法,而不是调用 drawRect:,该方法的实现必须将位图直接分配给图层的内容属性。这是 AppKit 期望您直接设置视图层对象的内容的一种情况。
同样在 NSView.h 中,updateLayer 的文档是
/* 支持图层的视图:如果视图对 WantUpdateLayer 的响应为“是”,则将调用 updateLayer,而不是 drawRect:。应该使用此方法以获得更好的性能;使用共享图像直接设置 layer.contents 并通知它如何使用 layer.contentsCenter 属性进行拉伸而不是使用 drawRect: 绘制到上下文中会更快。通常,还应该在 init 方法中将 layerContentsRedrawPolicy 设置为适当的值(通常需要 NSViewLayerContentsRedrawOnSetNeedsDisplay)。为了表示图层内容的刷新,然后调用 [view setNeedsDisplay:YES],当图层需要其内容时,将延迟调用 -updateLayer。在此方法期间不应更改几何图形或添加/删除子视图(或图层)。要添加子视图(或图层),请使用 -layout。即使未启用自动布局,-layout 仍将被调用,并且 wantUpdateLayer 返回 YES。
*/