Ita*_*atz 72 iphone delegates core-animation ios
我有一个类,它是一个子类UIView
.我可以通过实现drawRect
方法或通过实现drawLayer:inContext:
哪个是委托方法在视图中绘制内容CALayer
.
我有两个问题:
如果我实现drawLayer:inContext:
,它被调用(并且drawRect
不是,至少就断点可以告诉),即使我没有CALayer
通过使用以下方式将我的视图指定为委托:
[[self layer] setDelegate:self];
如果我的实例未被定义为图层的委托,那么如何调用委托方法?什么机制可以防止drawRect
被drawLayer:inContext:
调用?
Nat*_*ror 72
如何决定使用哪种方法?每个都有一个用例吗?
始终使用drawRect:
,永远不要使用任何UIView
作为绘图委托CALayer
.
如果我的实例未被定义为图层的委托,那么如何调用委托方法?什么机制阻止drawRect被
drawLayer:inContext:
调用?
每个UIView
实例都是其支持的绘图委托CALayer
.这就是为什么[[self layer] setDelegate:self];
似乎什么都不做.这是多余的.该drawRect:
方法实际上是视图层的绘图委托方法.在内部,UIView
实现drawLayer:inContext:
它执行一些自己的东西,然后调用drawRect:
.您可以在调试器中看到它:
这就是drawRect:
你实施时从未调用过的原因drawLayer:inContext:
.这也是为什么你永远不应该CALayer
在自定义UIView
子类中实现任何绘图委托方法的原因.您也应该永远不要查看另一个图层的绘图委托.这将导致各种古怪.
如果您正在实施,drawLayer:inContext:
因为您需要访问CGContextRef
,您可以drawRect:
通过调用从内部获取UIGraphicsGetCurrentContext()
.
que*_*ish 44
drawRect
应该只在绝对需要时实施.默认实现drawRect
包括许多智能优化,例如智能缓存视图的渲染.覆盖它会绕过所有这些优化.那很糟.有效地使用图层绘制方法几乎总是优于自定义drawRect
.Apple 经常使用a UIView
作为委托CALayer
- 实际上,每个UIView都是它的层的委托.您可以在几个Apple示例中看到如何在UIView中自定义图层绘图,包括(此时)ZoomingPDFViewer.
虽然使用drawRect
是常见的,但至少从2002/2003年IIRC开始,这种做法已被劝阻.沿着这条道路走的路没有很多好理由.
iPhone OS上的高级性能优化(幻灯片15)
小智 12
以下是Apple的Sample ZoomingPDFViewer的代码:
-(void)drawRect:(CGRect)r
{
// UIView uses the existence of -drawRect: to determine if it should allow its CALayer
// to be invalidated, which would then lead to the layer creating a backing store and
// -drawLayer:inContext: being called.
// By implementing an empty -drawRect: method, we allow UIKit to continue to implement
// this logic, while doing our real drawing work inside of -drawLayer:inContext:
}
-(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
...
}
Run Code Online (Sandbox Code Playgroud)
是否使用drawLayer(_:inContext:)
或drawRect(_:)
(或两者)使用自定义绘图代码取决于在动画时是否需要访问图层属性的当前值.
今天我在实现自己的Label类时遇到了与这两个函数相关的各种渲染问题.在查看文档,进行一些反复试验,反编译UIKit并检查Apple的Custom Animatable Properties示例后,我对它的工作方式有了很好的理解.
drawRect(_:)
如果您不需要在动画期间访问图层/视图属性的当前值,则只需使用它drawRect(_:)
来执行自定义绘图.一切都会好起来的.
override func drawRect(rect: CGRect) {
// your custom drawing code
}
Run Code Online (Sandbox Code Playgroud)
drawLayer(_:inContext:)
比方说,您想backgroundColor
在自定义绘图代码中使用:
override func drawRect(rect: CGRect) {
let colorForCustomDrawing = self.layer.backgroundColor
// your custom drawing code
}
Run Code Online (Sandbox Code Playgroud)
当您测试代码时,您会注意到backgroundColor
动画在飞行中时不会返回正确的值(即当前值).相反,它返回最终值(即动画完成时的值).
要在动画期间获取当前值,您必须访问传递给backgroundColor
的layer
参数drawLayer(_:inContext:)
.而且你还必须绘制context
参数.
知道视图self.layer
和layer
传递给的参数drawLayer(_:inContext:)
并不总是同一层是非常重要的!后者可能是前者的副本,部分动画已经应用于其属性.这样您就可以访问正在进行的动画的正确属性值.
现在绘图按预期工作:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
Run Code Online (Sandbox Code Playgroud)
但是有两个新问题:setNeedsDisplay()
几个属性喜欢backgroundColor
并且opaque
不再适用于您的视图.UIView
不再将呼叫和更改转发到其自己的层.
setNeedsDisplay()
只有你的视图实现时才会做某事drawRect(_:)
.如果函数实际执行某些操作并不重要,但UIKit会使用它来确定您是否进行自定义绘图.
这些属性可能不再起作用,因为不再调用UIView
自己的实现drawLayer(_:inContext:)
.
所以解决方案非常简单.只需调用超类的实现drawLayer(_:inContext:)
并实现一个空的drawRect(_:)
:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
override func drawRect(rect: CGRect) {
// Although we use drawLayer(_:inContext:) we still need to implement this method.
// UIKit checks for its presence when it decides whether a call to setNeedsDisplay() is forwarded to its layer.
}
Run Code Online (Sandbox Code Playgroud)
drawRect(_:)
只要您没有在动画期间属性返回错误值的问题,请使用:
override func drawRect(rect: CGRect) {
// your custom drawing code
}
Run Code Online (Sandbox Code Playgroud)
使用drawLayer(_:inContext:)
, drawRect(_:)
如果需要在动画时访问视图/图层属性的当前值:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
override func drawRect(rect: CGRect) {
// Although we use drawLayer(_:inContext:) we still need to implement this method.
// UIKit checks for its presence when it decides whether a call to setNeedsDisplay() is forwarded to its layer.
}
Run Code Online (Sandbox Code Playgroud)
在iOS上,视图与其图层之间的重叠非常大.默认情况下,视图是其图层的委托,并实现图层的drawLayer:inContext:
方法.根据我的理解,drawRect:
并drawLayer:inContext:
在这种情况下或多或少等同.可能是drawLayer:inContext:
调用的默认实现drawRect:
,或者drawRect:
仅drawLayer:inContext:
在您的子类未实现时调用.
如何决定使用哪种方法?每个都有一个用例吗?
这并不重要.为了遵循惯例,我通常会使用drawRect:
并保留drawLayer:inContext:
当我实际绘制不属于视图的自定义子图层时的使用.