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

时间:2018-05-12 19:04:06

标签: cocoa nsview drawrect nsscrollview

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

每个子视图通常有各种控件和一个特殊的子类子视图,它们显示计算密集的图形,在第一次调用-drawRect方法时进行预计算和缓存。因此,滚动浏览所有这些会分配计算和缓存,直到用户需要查看它为止。这一切都运作良好,并已多年来这样做。没有子视图'直到子子视图的一部分在滚动期间在层次结构中变得可见时,才会调用-drawRect个方法。

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

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

如何调试这种情况,这看起来完全违背了视图系统应该自动执行的操作?当视图的框架完全无法显示任何内容时,系统在什么情况下会调用视图-drawRect? Apple的框架方面有什么变化吗?

1 个答案:

答案 0 :(得分:0)

这可能是由于“响应式滚动”,这是在10.9中引入的,仅适用于针对10.8 SDK或更高版本链接的应用。最好的合并文档可能在10.9 AppKit release notes中。请务必阅读文档中的“透支”部分,其中(如果我是对的)是要求您绘制隐形视图的具体原因。

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

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

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