iOS 12, Xcode 10: UIView setNeedsDisplay(_:) 好像坏了

iOS 12, Xcode 10: UIView setNeedsDisplay(_:) seems to be broken

更新到 Xcode10 后,我意识到在我的应用程序中我的自定义 UIView(class 派生自 UIView)的 draw(_ rect: CGRect) 例程被错误地调用 rect.事实上,它总是被称为 rect 作为底层 UIView 的完整框架,而不是 setNeedsDisplay(_ rect: CGRect) 指定的 rect

这是一个可以 运行 作为游乐场的代码片段,它至少在我的设置中显示了上述在简约设置中的错误行为:

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)
test
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 占用空间小)。为了解决这个问题,动态后备存储功能通过绘制视图的全部内容来工作,然后计算出它需要多少内存,而不是从一开始就假设所有内容都需要广泛的颜色支持。

这样做的连锁反应是您无法重新绘制视图的较小子部分,因为这可能会改变这家商店。

如何绕过它

这是一个很棒的新功能,但如果您确实需要解决它,您可以在您的视图中禁用动态后备存储。您这样做的方法是显式设置视图 layer.

contentsFormat 属性

灰度、RGBA 8 位和 RGBA 16 位(广色)有 3 个选项供您选择

所以只要打电话:

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