实现drawRect:使整个视图更新



最近,我使用Apple的Quartz Debug来检测macOS中的屏幕更新。

据我所知,在NSView中,脏矩形参数- (void)drawRect:(NSRect)dirtyRect表示要更新的视图部分。

在我新创建的项目中,我创建了一个简单的"CustomView",它的实现非常简单,如下所示:

#import "CustomView.h"
@implementation CustomView
- (void)drawRect:(NSRect)dirtyRect {
NSLog(@"dirtyRect:%@", NSStringFromRect(dirtyRect));
[[NSColor blueColor] set];
NSRectFill(dirtyRect);
}
@end

比,我创建了一个按钮:

- (IBAction)testButtonClick:(id)sender {
[self.customView setNeedsDisplayInRect:NSMakeRect(1, 1, 50, 50)];
}

当我单击该按钮时,我的控制台中有以下日志:

2017-08-07 16:11:01.914859+0800 TestScreenUpdate[17818:728751] dirtyRect:{{1, 1}, {50, 50}}

还不错。 但是当我启用石英调试来检测屏幕更新时,每次单击该按钮时,整个自定义视图都会更新。
这是 gif 屏幕截图。

苹果的DragItemAround项目没有这个问题,它只更新脏部分。

而我的项目由Xcode 8.3.3创建,没有任何特殊配置。

也许drawRect:上的调用堆栈可以解释一些事情:

这是我项目中drawRect:的调用堆栈:

#0  0x00000001000017d4 in -[CustomView drawRect:]
#1  0x00007fffa4073513 in -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] ()
#2  0x00007fffa4072eb9 in __46-[NSView(NSLayerKitGlue) drawLayer:inContext:]_block_invoke ()
#3  0x00007fffa40729e1 in -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] ()
#4  0x00007fffa40723a6 in -[NSView(NSLayerKitGlue) drawLayer:inContext:] ()
#5  0x00007fffabf4c5a5 in CABackingStoreUpdate_ ()
#6  0x00007fffac06b558 in ___ZN2CA5Layer8display_Ev_block_invoke ()
#7  0x00007fffac06b070 in CA::Layer::display_() ()

这是苹果的DragItemAround项目中drawRect:的调用堆栈:

#0  0x0000000100001007 in -[DraggableItemView drawRect:]
#1  0x00007fffa406cf99 in -[NSView _drawRect:clip:] ()
#2  0x00007fffa40bcf2f in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] ()
#3  0x00007fffa40bd39a in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] ()
#4  0x00007fffa40bd39a in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] ()
#5  0x00007fffa406aad2 in -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] ()
#6  0x00007fffa406a2af in -[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] ()

您可能为视图或其祖先之一启用了核心动画图层。我认为当您更新图层时,窗口服务器会看到整个内容正在更新,即使您只重绘了其中的一部分。

默认情况下,最新版本的 Xcode 中的 Cocoa App 模板在根视图中启用图层。旧版本的 Xcode 中的模板没有,Apple 的许多示例项目都是使用旧版本的 Xcode 创建的。

在情节提要或 xib 中选择视图。然后选取"显示>实用工具">"显示视图效果检查器"。如果为视图或其任何祖先启用了"核心动画图层",则会将视图绘制到图层中。您可以尝试禁用它以查看它是否有所作为。

最新更新