如何防止 Cocoa 在每个完全不可见的子视图上调用 drawRect



我有一个完全工作,非常快速的单窗口应用程序,用Objective-C编写,最初使用XCode 4在MacOS 10.7上开发。 它具有单个滚动视图,显示具有许多子视图的一个大型内容视图的部分。 仅显示每个子视图的一部分,并且只有最初可见的子视图才能在应用显示初始窗口时调用其-drawRect方法。 正在滚动的内容视图及其所有子视图和子子视图是在应用启动时以编程方式创建的(而不是从 xib 文件创建的(。

每个子视图通常都有各种控件和一个特殊的子类化子视图,该子视图显示计算密集型图形,该图形在首次调用其-drawRect方法时进行预计算和缓存。 因此,滚动浏览所有这些会分发计算和缓存,直到用户需要看到它。 这一切都运作良好,并且多年来一直如此。 在子子视图具有在滚动期间在层次结构中可见的自身部分之前,不会调用任何子子视图的-drawRect方法。

问题是我现在已经将应用程序移植到 XCode 9.3 和 MacOS 10.13.4,并且发现在桌面上启动时首次显示带有滚动视图的单个窗口后,每个子子视图都有其-drawRect方法由 Cocoa 的运行时调用,即使没有一个子子视图在空间上接近于在滚动视图内的视图层次结构中可见。窗。 这会导致每个子子视图预先计算和缓存,所有这些都在用户可以与刚刚启动的应用交互之前完成。 这需要几秒钟,这在用户界面方面是不可接受的。

我不知道我未更改的代码可能会做什么,导致所有不可见的子子视图被要求自己绘制,因此似乎这种新状态可能是由于后来的一些Apple框架行为更改。 我尝试查询-drawRect内部子视图的-isHiddenOrHasHiddenAncestor属性,但它总是返回 false,可能是因为超级视图在逻辑上可见,但被滚动视图剪裁了。

如何调试这种情况,这似乎与视图系统应该自动执行的操作完全相反? 当视图的框架完全无法显示任何内容时,系统在什么情况下调用视图的-drawRect? 苹果的框架方面有什么变化吗?

这可能是由于"响应式滚动",这是在 10.9 中引入的,并且仅适用于链接到 10.8 SDK 或更高版本的应用。最好的整合文档可能在 10.9 AppKit 发行说明中。请务必阅读文档下方的"透支"部分,这(如果我是对的(是要求绘制您的隐形视图的具体原因。

防止这种情况的快速但肮脏的方法是让其中一个相关视图的类覆盖+isCompatibleWithResponsiveScrolling返回 false。

但是,最好在后台线程上异步执行计算。在计算完成之前,您的视图会绘制一些便宜的占位符(或什么都没有(。完成后,您会将其标记为需要显示。(请务必将所有 UI 工作从后台线程分流回主线程。

您还可以推迟子视图(或子子视图(的创建,并根据需要使用-prepareContentInRect:的覆盖来创建它们。或者使用该方法的重写来禁用过度绘制,如发行说明中所述。

最新更新