我有一个 macOS 应用程序,其视图占据整个窗口,并在窗口的 styleMask 中设置了 NSWindowStyleMaskFullSizeContentView。我想在鼠标进入窗口时显示标题栏和窗口按钮,并在鼠标退出窗口时隐藏它们。
我使用的跟踪区域的矩形为 window.contentView.bounds,并从标题栏的高度中减去标题栏的高度。
我已经实现了 mouseEntered: 和 mouseExited: 方法来处理显示/隐藏操作。
我遇到的问题是,当我将鼠标移动到窗口的标题栏时,应用程序快速连续接收多个 mouseExited:/mouseEntered: 事件,导致标题栏出现“颤动”效果。
为了重现这个问题,我首先在 Xcode 中创建了一个 Objective-C 项目,并使用 IB 将窗口属性设置为“全尺寸内容视图”。然后如图所示编辑ViewController.m。
//
// ViewController.m
// Repro
//
#import "ViewController.h"
@implementation ViewController
{
NSTrackingArea *trackingArea;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)viewWillAppear
{
// background color
self.view.wantsLayer = true;
self.view.layer.backgroundColor = [NSColor systemBlueColor].CGColor;
// tracking area
[self setupTrackingArea:self.view.window.contentView.bounds];
return;
}
- (void)setupTrackingArea:(CGRect)frame
{
if (trackingArea)
{
[self.view removeTrackingArea:trackingArea];
}
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect | NSTrackingMouseMoved;
// subtract the titlebar height from the content view height
frame.size.height -= [self titlebarHeight:self.view.window];
trackingArea = [[NSTrackingArea alloc]initWithRect:frame
options:options
owner:self
userInfo:nil];
[self.view addTrackingArea:trackingArea];
}
- (CGFloat)titlebarHeight:(NSWindow *)window
{
CGFloat windowHeight = window.contentView.frame.size.height;
CGFloat contentHeight = window.contentLayoutRect.size.height;
CGFloat titlebarHeight = windowHeight - contentHeight;
return titlebarHeight;
}
- (void)mouseEntered:(NSEvent *)event
{
[self showTitleBar];
[self showWindowButtons:self.view.window];
return;
}
- (void)mouseExited:(NSEvent *)event
{
[self hideTitleBar];
[self hideWindowButtons:self.view.window];
return;
}
- (void)showTitleBar
{
self.view.window.titlebarAppearsTransparent = false;
self.view.window.title = @"Repro";
return;
}
- (void)hideTitleBar
{
self.view.window.titlebarAppearsTransparent = true;
self.view.window.title = @"";
return;
}
- (void)hideWindowButtons:(NSWindow *)window
{
[window standardWindowButton:NSWindowZoomButton].hidden = true;
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = true;
[window standardWindowButton:NSWindowCloseButton].hidden = true;
return;
}
- (void)showWindowButtons:(NSWindow *)window
{
[window standardWindowButton:NSWindowZoomButton].hidden = false;
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = false;
[window standardWindowButton:NSWindowCloseButton].hidden = false;
return;
}
@end
感谢 Willeke 的建议(非常感谢您的帮助!),有一个解决方案。它涉及 NSView 的子类化(我称之为 MyView),将所有跟踪区域处理移至该视图中,并将其从 ViewController 中删除。 MyView.m 是:
//
// MyView.m
// Repro
//
#import "MyView.h"
@implementation MyView
{
NSTrackingArea *trackingArea;
}
- (void)viewWillMoveToWindow:(NSWindow *)newWindow
{
if (newWindow)
{
newWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
[self setupTrackingArea:newWindow];
}
[super viewWillMoveToWindow:newWindow];
return;
}
- (void)updateTrackingAreas
{
[self setupTrackingArea:self.window];
[super updateTrackingAreas];
return;
}
- (void)setupTrackingArea:(NSWindow *)window
{
if (trackingArea)
[self removeTrackingArea:trackingArea];
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways;
trackingArea = [[NSTrackingArea alloc]initWithRect:self.bounds
options:options
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
return;
}
- (void)mouseEntered:(NSEvent *)event
{
[self showTitleBar];
[self showWindowButtons:self.window];
return;
}
- (void)mouseExited:(NSEvent *)event
{
[self hideTitleBar];
[self hideWindowButtons:self.window];
return;
}
- (void)showTitleBar
{
self.window.titlebarAppearsTransparent = false;
self.window.title = [NSProcessInfo processInfo].processName;
return;
}
- (void)hideTitleBar
{
self.window.titlebarAppearsTransparent = true;
self.window.title = @"";
return;
}
- (void)hideWindowButtons:(NSWindow *)window
{
// hide the minimize and zoom buttons (yellow and green)
[window standardWindowButton:NSWindowZoomButton].hidden = true;
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = true;
[window standardWindowButton:NSWindowCloseButton].hidden = true;
return;
}
- (void)showWindowButtons:(NSWindow *)window
{
[window standardWindowButton:NSWindowZoomButton].hidden = false;
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = false;
[window standardWindowButton:NSWindowCloseButton].hidden = false;
return;
}
@end