You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 00:51:24