更新到Xcode 10后,我意识到我的应用程序中的自定义UIView(来自UIView的类)的draw(_ rect: CGRect)
例程被调用了错误的rect
。实际上它总是被称为rect
是底层UIView的完整框架,而不是由rect
指定的setNeedsDisplay(_ rect: CGRect)
。
这是一个可以作为游乐场运行的代码段,至少在我的设置中,它在简约设置中显示了上述错误行为:
import Foundation
import UIKit
import PlaygroundSupport
class CustomView: UIView {
override func draw(_ rect: CGRect) {
print("rect = \(rect)")
}
}
let customView = CustomView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 200.0, height: 200.0)))
PlaygroundPage.current.liveView = customView
print("test")
customView.setNeedsDisplay(CGRect(origin: CGPoint.zero, size: CGSize(width: 100.0, height: 100.0)))
我得到的输出是
rect =(0.0,0.0,200.0,200.0) 测试 rect =(0.0,0.0,200.0,200.0)
rect的第一个打印输出是视图的标准完全重绘,但是打印“test”后的第二个输出会产生问题。由于之前调用customView.setNeedsDisplay
而导致的输出是重绘,应该是较小的指定矩形(0.0, 0.0, 100.0, 100.0)
。
所以我显而易见的问题是:
我在Xcode 9,10和10.1中进行了测试。
iOS 11和iOS 12 / 12.1之间的行为已经发生了明显变化
文档或头文件中没有任何迹象表明这是故意的。
对我来说看起来像个错误。
这实际上是iOS 12的新动态后备存储功能的故意。
后备存储是存储绘制视图的内容,并且需要分配的内存来执行此操作。该内存量取决于视图的大小,因为它本质上是颜色和像素之间的映射。
如果您要绘制灰度图像,但已为宽色域指定了内存,那么将导致大量空的指定内存(灰度级具有较低的RGBA占用空间)。为了解决这个问题,动态后备存储功能通过绘制视图的整个内容来工作,然后确定它需要多少内存,而不是假设从一开始就需要广泛的颜色支持。
这样做的结果是你无法重新绘制视图的较小子部分,因为这可能会改变这个商店。
这是一个很棒的新功能,但如果您确实需要解决它,可以在视图上禁用动态后备存储。你这样做的方法是明确设置视图contentsFormat
的layer
属性。
您可以选择三种与灰度相关的选项,RGBA 8位和RGBA 16位(宽色)
所以请致电:
layer.contentsFormat = .RGBA16Float
而你的setNeedsDisplay(_ rect: CGRect)
将再次按预期开始工作
您可以在这里阅读酒店:https://developer.apple.com/documentation/quartzcore/calayer/1792104-contentsformat
WWDC 18也有一个很好的演讲,解释了新的动态后备存储和(非常安静地)提到这种技术
https://developer.apple.com/videos/play/wwdc2018/219/?time=1451