Monterey系统下NSButton的#selector(ViewController.backBtnClicked)点击事件失效问题
我碰到过不少开发者在升级到Monterey后遇到这个问题——这确实是苹果在WKWebView的PDF渲染组件里新增底部操作栏后带来的已知Bug,这个操作栏的事件响应范围出现了异常,会意外覆盖到WebView上方的区域,导致你的返回按钮点击事件被拦截;而加载HTML时没有这个问题,是因为HTML页面不会触发这个内置的PDF操作栏。
下面给你几个实用的解决办法,按稳妥程度排序:
1. 用JavaScript隐藏PDF底部操作栏(无审核风险)
推荐优先用这个方法,不需要触碰私有API,通过注入WKUserScript修改PDF渲染页面的样式,直接隐藏操作栏:
// 初始化WebView配置时添加这个脚本 let config = WKWebViewConfiguration() let hidePDFToolbarScript = WKUserScript( source: """ document.addEventListener('DOMContentLoaded', () => { // 找到底部操作栏并隐藏 const toolbar = document.querySelector('.toolbar'); if (toolbar) toolbar.style.display = 'none'; // 调整PDF容器高度,避免底部留空 const viewer = document.getElementById('viewer'); if (viewer) viewer.style.bottom = '0'; }); """, injectionTime: .atDocumentEnd, forMainFrameOnly: true ) config.userContentController.addUserScript(hidePDFToolbarScript) // 用这个配置初始化你的WKWebView let webView = WKWebView(frame: .zero, configuration: config)
这个方法既解决了事件拦截问题,又不会影响App Store审核。
2. 调整视图层级,确保按钮优先接收事件
把你的返回按钮放在一个独立的NSView上,让这个View的层级高于WKWebView,同时开启layer支持,保证事件不会被WebView拦截:
// 先给WebView和按钮创建一个容器View let containerView = NSView(frame: self.view.bounds) self.view.addSubview(containerView) // 添加WKWebView到容器底部 containerView.addSubview(webView) webView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ webView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), webView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), webView.topAnchor.constraint(equalTo: containerView.topAnchor), webView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) ]) // 创建按钮容器View,放在WebView上层 let buttonHolderView = NSView(frame: CGRect(x: 20, y: containerView.bounds.height - 50, width: 30, height: 30)) buttonHolderView.wantsLayer = true // 开启layer保证事件优先级 containerView.addSubview(buttonHolderView) // 添加你的返回按钮到这个容器里 buttonHolderView.addSubview(backBtn) backBtn.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ backBtn.centerXAnchor.constraint(equalTo: buttonHolderView.centerXAnchor), backBtn.centerYAnchor.constraint(equalTo: buttonHolderView.centerYAnchor) ])
通过层级分离,按钮的点击事件会被优先处理,不会被WebView的PDF操作栏抢走。
3. 重写hitTest强制分发按钮事件
如果上面两个方法都不生效,你可以在ViewController里重写hitTest(_:)方法,手动判断点击位置是否在按钮范围内,强制让按钮接收事件:
override func hitTest(_ point: NSPoint) -> NSView? { // 把点击点转换到按钮的坐标系 let btnPoint = backBtn.convert(point, from: self.view) // 如果点击点在按钮范围内,返回按钮 if backBtn.bounds.contains(btnPoint) { return backBtn } // 否则交给父类处理 return super.hitTest(point) }
这个方法是最后的兜底方案,直接从事件分发层面绕过WebView的拦截。
另外,你也可以在苹果开发者平台提交Bug反馈,这个问题确实是Monterey引入的,后续系统更新可能会修复这个操作栏的响应范围问题。
内容的提问来源于stack exchange,提问作者neelabh
相关产品推荐
相关产品推荐

