SwiftUI集成Unity播放器关闭后无响应、窗口层级异常问题
问题根因
你当前调用ufw.showUnityWindow()时,Unity框架会主动创建独立的UIWindow并将其置于应用窗口层级最顶端,完全脱离你通过fullScreenCover弹出的SwiftUI视图链路。当你触发关闭逻辑时,仅修改了SwiftUI的状态收起全屏弹窗,但盖在最上层的Unity独立窗口没有被移除,所有用户触摸事件都会被这个残留窗口拦截,底层SwiftUI界面自然完全无响应,也不会抛出常规错误日志。
修复方案
核心逻辑是放弃让Unity自行管理窗口,把Unity渲染视图直接挂载到SwiftUI提供的宿主控制器上,让视图生命周期和fullScreenCover绑定,同时不要在每次关闭时卸载Unity实例,避免状态异常:
- 调整UnityBridge逻辑:移除
showUnityWindow()调用,改为手动将Unity渲染视图添加到宿主控制器,新增专门的暂停/关闭方法,非应用退出场景不执行Unity实例销毁 - 调整UIViewControllerRepresentable逻辑:移除延迟异步挂载的逻辑,在控制器视图挂载到窗口后再注入Unity视图,新增销毁时的自动清理逻辑
- 调整外层SwiftUI逻辑:监听Unity侧的关闭通知,同步更新弹窗状态,确保全屏弹窗和Unity视图同步销毁
修改后的UnityBridge.swift
// 关闭通知常量统一维护,避免拼写错误 let closeUnityPlayerNotification = "com.yourapp.closeUnityPlayer" class UnityBridge: NSObject, UnityFrameworkListener { static var instance: UnityBridge? let ufw: UnityFramework weak var hostController: UIViewController? var unityView: UIView? private override init() { // 原有UnityFramework初始化逻辑保留 ufw = UnityFramework.getInstance() super.init() ufw.registerFrameworkListener(self) } static func getInstance() -> UnityBridge { if instance == nil { instance = UnityBridge() } return instance! } /// 挂载Unity视图到传入的宿主控制器 public func show(hostController: UIViewController) { self.hostController = hostController // 绑定到宿主控制器的window,禁止Unity自行创建独立窗口 ufw.setUnityWindow(hostController.view.window) guard let unityRootView = ufw.appController()?.rootView else { return } unityView = unityRootView unityRootView.frame = hostController.view.bounds unityRootView.autoresizingMask = [.flexibleWidth, .flexibleHeight] hostController.view.addSubview(unityRootView) ufw.pause(false) } /// 供Unity侧调用的关闭方法,日常进出不执行unload逻辑 @objc public func closeUnityPlayer() { ufw.pause(true) unityView?.removeFromSuperview() unityView = nil hostController = nil NotificationCenter.default.post(name: Notification.Name(closeUnityPlayerNotification), object: nil) } // 仅在应用需要完全销毁Unity实例时触发unload回调,日常关闭不走到这步 internal func unityDidUnload(_ notification: Notification!) { ufw.unregisterFrameworkListener(self) UnityBridge.instance = nil } }
修改后的UnityPlayer.swift
struct UnityPlayer: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { UIViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { // 等控制器真正挂载到窗口后再加载Unity视图,避免拿不到window实例 guard uiViewController.view.window != nil else { return } let unity = UnityBridge.getInstance() // 避免重复挂载视图 if unity.unityView?.superview != uiViewController.view { unity.show(hostController: uiViewController) } } // SwiftUI销毁该控制器时自动清理Unity视图 static func dismantleUIViewController(_ uiViewController: UIViewController, coordinator: ()) { UnityBridge.getInstance().closeUnityPlayer() } }
修改外层SwiftUI唤起逻辑
//Some SwiftUI View NavigationView { SomeView() .fullScreenCover(isPresented: $applicationState.unityPlayerActive, onDismiss: { // 弹窗关闭后确保Unity处于暂停状态,避免后台占用资源 UnityBridge.getInstance().ufw.pause(true) }, content: { UnityPlayer() .ignoresSafeArea() }) .onReceive(NotificationCenter.default.publisher(for: Notification.Name(closeUnityPlayerNotification))) { _ in // 收到Unity侧关闭请求时,同步更新SwiftUI状态收起全屏弹窗 applicationState.unityPlayerActive = false } }
注意事项:Unity侧触发关闭时,直接通过原生桥接调用
closeUnityPlayer()即可,不要调用UnityFramework.unloadApplication()。完全卸载Unity的逻辑仅在应用退出时执行即可,日常进出使用暂停/恢复逻辑,既可以避免窗口残留问题,也能大幅缩短下次唤起Unity的加载时间,减少内存异常概率。
内容的提问来源于stack exchange,提问作者Akhu
相关产品推荐
相关产品推荐

