首页 > 解决方案 > iOS 12、Xcode 10:UIView setNeedsDisplay(_:) 似乎坏了

问题描述

更新到 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)))

我得到的输出是

矩形 = (0.0, 0.0, 200.0, 200.0)
测试
矩形 = (0.0, 0.0, 200.0, 200.0)

rect 的第一个打印输出是视图的标准完整重绘,但打印“test”后的第二个输出会产生问题。由于customView.setNeedsDisplay之前调用而重绘的输出应该是较小的指定矩形(0.0, 0.0, 100.0, 100.0)

所以我明显的问题是:

标签: iosswiftxcode10ios12

解决方案


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

什么是后备商店

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

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

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

如何绕过它

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


推荐阅读