You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NSWindow全屏内容视图下mouseEntered/mouseExited事件抖动问题求助

解决macOS全屏内容视图窗口标题栏显示/隐藏时的抖动问题

问题根源

  1. 跟踪区域失效:切换标题栏显示状态时,窗口的contentLayoutRect会发生变化,之前基于初始contentView.bounds设置的跟踪区域不再准确,鼠标移动时会反复触发进入/退出事件。
  2. 无状态控制:每次触发mouseEntered:或mouseExited:都会强制执行显示/隐藏操作,即使当前状态已经符合预期,导致UI频繁刷新出现抖动。

修复代码

//
//  ViewController.m
//  Repro
//

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, assign) BOOL isTitleBarVisible;
@end

@implementation ViewController
{
    NSTrackingArea  *trackingArea;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.isTitleBarVisible = NO;
    // 监听窗口布局变化,更新跟踪区域
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(windowLayoutChanged:)
                                                 name:NSWindowDidResizeNotification
                                               object:self.view.window];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(windowLayoutChanged:)
                                                 name:NSWindowDidMoveNotification
                                               object:self.view.window];
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewWillAppear
{
    // background color
    self.view.wantsLayer = true;
    self.view.layer.backgroundColor = [NSColor systemBlueColor].CGColor;

    // 初始化跟踪区域
    [self setupTrackingArea:self.view.window.contentView.bounds];
    
    // 初始隐藏标题栏
    [self hideTitleBar];
    [self hideWindowButtons:self.view.window];
}

- (void)windowLayoutChanged:(NSNotification *)note
{
    // 窗口布局变化时更新跟踪区域
    [self setupTrackingArea:self.view.window.contentView.bounds];
}

- (void)setupTrackingArea:(CGRect)frame
{
    if (trackingArea)
    {
        [self.view removeTrackingArea:trackingArea];
    }

    NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect;

    // 减去标题栏高度,确保跟踪区域只覆盖标题栏以下的内容区域
    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;
    return windowHeight - contentHeight;
}

- (void)mouseEntered:(NSEvent *)event
{
    if (!self.isTitleBarVisible) {
        [self showTitleBar];
        [self showWindowButtons:self.view.window];
        self.isTitleBarVisible = YES;
    }
}

- (void)mouseExited:(NSEvent *)event
{
    // 获取鼠标当前位置,判断是否在标题栏区域内
    NSPoint globalPoint = event.locationInWindow;
    NSPoint localPoint = [self.view.window convertPoint:globalPoint toView:self.view.window.contentView];
    
    CGFloat titleBarHeight = [self titlebarHeight:self.view.window];
    if (localPoint.y >= self.view.window.contentView.bounds.size.height - titleBarHeight) {
        // 鼠标在标题栏区域,不隐藏
        return;
    }
    
    if (self.isTitleBarVisible) {
        [self hideTitleBar];
        [self hideWindowButtons:self.view.window];
        self.isTitleBarVisible = NO;
    }
}

- (void)showTitleBar
{
    self.view.window.titlebarAppearsTransparent = false;
    self.view.window.title = @"Repro";
}

- (void)hideTitleBar
{
    self.view.window.titlebarAppearsTransparent = true;
    self.view.window.title = @"";
}

- (void)hideWindowButtons:(NSWindow *)window
{
    [window standardWindowButton:NSWindowZoomButton].hidden = true;
    [window standardWindowButton:NSWindowMiniaturizeButton].hidden = true;
    [window standardWindowButton:NSWindowCloseButton].hidden = true;
}

- (void)showWindowButtons:(NSWindow *)window
{
    [window standardWindowButton:NSWindowZoomButton].hidden = false;
    [window standardWindowButton:NSWindowMiniaturizeButton].hidden = false;
    [window standardWindowButton:NSWindowCloseButton].hidden = false;
}

@end

关键修改点

  • 状态变量isTitleBarVisible:避免重复执行显示/隐藏操作,减少UI刷新次数。
  • 监听窗口布局通知:在窗口大小变化、移动时重新计算跟踪区域,确保区域始终准确覆盖标题栏以下的内容区域。
  • 优化mouseExited:逻辑:判断鼠标是否处于标题栏区域,若是则不执行隐藏操作,防止鼠标停留在标题栏时触发不必要的隐藏。

内容的提问来源于stack exchange,提问作者user9276599

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:07:03