iOS 12、Xcode 10:UIView setNeedsDisplay(_:) 似乎已损坏

Wiz*_*ard 3 ios swift ios12 xcode10

更新到 Xcode 10 后,我意识到draw(_ rect: CGRect)我的应用程序中自定义 UIView(从 UIView 派生的类)的例程被调用了错误的rect. 事实上,它总是以rect底层 UIView 的完整框架被调用,而不是rectsetNeedsDisplay(_ 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)))
Run Code Online (Sandbox Code Playgroud)

我得到的输出是

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)

所以我的明显问题是:

  • 你能重现这种行为吗?
  • 我错过了一些明显的东西吗?
  • 这是一个错误吗?

小智 6

这实际上是 iOS 12 新的动态后备存储功能有意为之。

什么是后备店

后备存储用于存储绘制的视图,并且需要分配内存来执行此操作。该内存量取决于视图的大小,因为它本质上是颜色和像素之间的映射。

如果您要绘制灰度图像,但已为宽色域分配了内存,那么这将导致大量空分配内存(灰度的占用空间低于 RGBA)。为了解决这个问题,动态后备存储功能的工作原理是绘制视图的整个内容,然后计算它需要多少内存,而不是从一开始就假设所有内容都需要广泛的颜色支持。

这样做的后果是您无法重新绘制视图的较小子部分,因为这可能会更改此存储。

如何绕过它

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

您可以选择三个与灰度、RGBA 8 位和 RGBA 16 位(广色域)相关的选项

所以只要打电话:

layer.contentsFormat = .RGBA16Float
Run Code Online (Sandbox Code Playgroud)

setNeedsDisplay(_ rect: CGRect)将再次按预期开始工作

您可以在此处阅读该物业:https : //developer.apple.com/documentation/quartzcore/calayer/1792104-contentsformat

还有一个来自 WWDC 18 的精彩演讲,解释了新的动态后备存储,并且(非常安静地)提到了这种技术

https://developer.apple.com/videos/play/wwdc2018/219/?time=1451