NSMenu中NSMenuItem内按钮触发的NSPopover无法响应鼠标事件如何解决
问题原因
- NSMenu显示时会启动专属的事件跟踪循环,所有鼠标事件默认优先被该循环捕获,仅分发到菜单关联的视图对象。即使Popover的视觉层级更高,也不会被纳入菜单事件循环的分发范围,因此无法响应鼠标操作。
- 手动调用
makeKeyWindow/becomeKeyWindow不生效,是因为菜单事件跟踪循环的优先级高于普通窗口的Key状态判定规则,事件不会传递到菜单之外的普通窗口响应链。
解决方案
方案1(最简便,无兼容问题)
在弹出Popover前主动终止菜单的事件跟踪,只需要在showPopover:方法开头添加关闭菜单的代码即可,符合系统常规交互逻辑,无额外副作用:
- (void)showPopover:(NSButton *)sender { // 新增这一行,终止当前菜单的事件跟踪 [[NSMenu currentMenu] cancelTracking]; /// 原有逻辑保持不变 NSPopover *popover = [NSPopover new]; NSViewController *popoverVC = [NSViewController new]; NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 140, 25)]; button.title = @"现在可以正常点击了"; button.target = self; button.action = @selector(showPopover:); popoverVC.view = button; [popover setContentViewController:popoverVC]; [popover showRelativeToRect:sender.frame ofView:sender.superview preferredEdge:NSMaxYEdge]; }
方案2(需保留菜单显示时使用)
如果业务要求弹出Popover时菜单不关闭,可以手动调整Popover窗口的层级和事件接收权限:
- (void)showPopover:(NSButton *)sender { NSPopover *popover = [NSPopover new]; NSViewController *popoverVC = [NSViewController new]; NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 140, 25)]; button.title = @"现在可以正常点击了"; button.target = self; button.action = @selector(showPopover:); popoverVC.view = button; [popover setContentViewController:popoverVC]; // 设置Popover行为允许接收外部事件 popover.behavior = NSPopoverBehaviorTransient; [popover showRelativeToRect:sender.frame ofView:sender.superview preferredEdge:NSMaxYEdge]; // 调整Popover窗口优先级高于菜单,允许接收事件 NSWindow *popoverWindow = popover.contentViewController.view.window; popoverWindow.level = NSMainMenuWindowLevel + 1; popoverWindow.ignoresMouseEvents = NO; }
注意该方案需要额外处理Popover关闭后菜单的交互恢复逻辑,避免出现后续菜单无法点击的异常。
内容的提问来源于stack exchange,提问作者mark2mark
相关产品推荐
相关产品推荐

