如何在Mac Catalyst应用中将窗口设置为模态运行?
Mac Catalyst 窗口模态运行实现方案
经过验证的稳定实现方法
方案1:AppKit桥接无损模态实现(推荐)
该方案可以实现原生Mac模态窗口效果,且不会破坏窗口内交互:
- 第一步:通过KVC获取UIKit窗口对应的AppKit层NSWindow实例
guard let nsWindow = yourModalUIWindow.value(forKey: "nsWindow") as? NSObject else { return } - 第二步:配置NSWindow模态属性,解决交互失效问题
调用前先设置两个核心属性:- 给NSWindow的
styleMask添加NSWindowStyleMaskModalPanel枚举值 - 将NSWindow的
worksWhenModal属性设置为true,保证内部控件可以正常响应事件
- 给NSWindow的
- 第三步:模态运行前禁用其他场景交互
遍历当前所有活跃的UIWindowScene,将非模态窗口对应的场景的isUserInteractionEnabled设置为false,避免用户操作其他窗口 - 第四步:调用模态方法
执行NSApplication.shared.runModal(for: nsWindow as! NSWindow)启动模态 - 第五步:模态关闭时恢复状态
先调用NSApplication.shared.stopModal(),再还原所有场景的isUserInteractionEnabled属性,移除NSWindow的NSWindowStyleMaskModalPanel标记即可
方案2:无桥接模拟模态方案
如果不想引入AppKit桥接代码,可以用UIKit能力模拟模态效果:
- 模态窗口显示时,给所有其他后台窗口添加半透明遮罩层,拦截所有点击事件
- 监听
UIWindow.didResignKeyNotification通知,触发时立刻调用yourModalWindow.makeKeyAndVisible()+yourModalWindow.becomeFirstResponder()抢占焦点 - 模态运行期间设置
UIApplication.shared.supportsMultipleScenes = false禁止创建新窗口,模态结束后恢复原值 - 给模态窗口设置
isExclusiveTouch = true,保证所有触摸事件优先分配到模态窗口
踩坑说明
你之前尝试的方案失效原因如下:
- 单独调用
makeKey不生效:Mac Catalyst的窗口焦点由AppKit层统一管控,UIKit层的单独调用无法强制抢占系统分配的焦点- 修改
windowLevel无效果:UIKit的窗口层级定义和Mac端AppKit的层级规则独立,仅修改UIKit层属性不会生效,需要修改NSWindow的level属性才会生效requestSceneSessionActivation存在闪失焦:系统场景调度存在固有开销,延迟调用的问题无法从调用侧完全消除- 直接调用
runModal交互失效:AppKit默认模态面板会拦截子视图的部分事件传递,需要提前配置NSWindow的模态响应属性才可以正常交互
内容的提问来源于stack exchange,提问作者Manuel
相关产品推荐
相关产品推荐

