最近,我使用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 中选择视图。然后选取"显示>实用工具">"显示视图效果检查器"。如果为视图或其任何祖先启用了"核心动画图层",则会将视图绘制到图层中。您可以尝试禁用它以查看它是否有所作为。