寻找MacOS下等效于Windows EnableWindow的NSWindow事件拦截方法
macOS实现NSWindow全局拦截键盘、鼠标事件(等同Windows EnableWindow效果)的方案
macOS没有提供和Windows EnableWindow 完全对等的系统API,以下是两种可实现相同效果的成熟方案:
方案1:自定义NSWindow子类重写sendEvent:(最推荐)
该方案可以100%控制所有分发到窗口的事件,拦截范围覆盖鼠标、键盘、触控板等所有输入类型,灵活性最高。
@interface CustomControllableWindow : NSWindow /// 控制窗口是否可交互,对应EnableWindow的开关 @property (nonatomic, assign) BOOL isUserInteractionEnabled; @end @implementation CustomControllableWindow - (void)sendEvent:(NSEvent *)event { // 窗口禁用时直接拦截所有输入类事件,不向下分发 if (!self.isUserInteractionEnabled) { NSEventType eventType = event.type; // 可根据需求调整需要拦截的事件类型 if (eventType == NSEventTypeLeftMouseDown || eventType == NSEventTypeLeftMouseUp || eventType == NSEventTypeRightMouseDown || eventType == NSEventTypeRightMouseUp || eventType == NSEventTypeMouseMoved || eventType == NSEventTypeScrollWheel || eventType == NSEventTypeKeyDown || eventType == NSEventTypeKeyUp || eventType == NSEventTypeFlagsChanged) { return; } } [super sendEvent:event]; } @end
方案2:使用NSEvent本地事件监视器(无需子类化NSWindow)
适合不方便修改NSWindow继承关系的场景,通过全局本地监视器拦截指定窗口的所有输入事件。
// 需要声明为属性持有监视器对象,否则会被提前释放导致失效 @property (nonatomic, strong) id windowEventMonitor; /// 禁用窗口交互,对应EnableWindow(NO) - (void)disableWindowInteraction:(NSWindow *)targetWindow { NSEventMask monitorMask = NSEventMaskLeftMouseDown | NSEventMaskLeftMouseUp | NSEventMaskRightMouseDown | NSEventMaskRightMouseUp | NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskScrollWheel | NSEventTypeMouseMoved; self.windowEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:monitorMask handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { // 仅拦截目标窗口的事件 if (event.window == targetWindow) { return nil; // 返回nil表示丢弃该事件 } return event; }]; // 搭配系统原生方法作为补充兜底 [targetWindow setIgnoresMouseEvents:YES]; [targetWindow resignKeyWindow]; } /// 恢复窗口交互,对应EnableWindow(YES) - (void)enableWindowInteraction:(NSWindow *)targetWindow { if (self.windowEventMonitor) { [NSEvent removeMonitor:self.windowEventMonitor]; self.windowEventMonitor = nil; } [targetWindow setIgnoresMouseEvents:NO]; }
补充优化建议
- 禁用窗口时可在窗口最上层添加一层半透明灰色遮罩视图,视觉上明确提示窗口不可交互,和Windows禁用窗口的表现对齐
- 如果目标窗口包含子窗口、NSPanel或弹出的Sheet,需要同步对这些附属窗口执行相同的禁用逻辑,避免事件漏拦截
内容的提问来源于stack exchange,提问作者Roy Rashti
相关产品推荐
相关产品推荐

