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

iOS原生集成Unity视图层级问题:按钮被Unity视图遮挡

解决Unity视图遮挡原生按钮的问题

看起来你遇到的核心问题是Unity启动后会自动把自己的视图放到整个窗口的最顶层,直接覆盖了你的原生按钮。咱们一步步来分析和解决:

问题根源

  1. Unity的视图层级抢占:你调用appDelegate.startUnity()时,内部的applicationDidBecomeActive方法会触发Unity的初始化逻辑,它会自动将Unity视图添加到UIWindow的最上层,完全绕过了你在showUnitySubView里把视图插入到at:0的操作——毕竟窗口层级比视图层级优先级更高。
  2. 停止逻辑不完整:你的stopUnity()只调用了Unity的生命周期方法,没有把Unity视图从父视图中移除,所以即使Unity停止运行,视图依然会留在界面上遮挡按钮。

修复方案

1. 调整Unity视图的添加逻辑,确保按钮在顶层

修改Unity3DViewController里的showUnitySubView方法,先把Unity视图绑定到当前ViewController的视图上,再强制把按钮移到最上层:

func showUnitySubView() {
    guard let unityView = UnityGetGLView() else { return }
    
    // 先移除Unity视图可能存在的父视图,避免重复添加
    unityView.removeFromSuperview()
    
    // 添加到当前ViewController的视图中,并设置铺满约束
    view.addSubview(unityView)
    unityView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        unityView.topAnchor.constraint(equalTo: view.topAnchor),
        unityView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        unityView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        unityView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
    ])
    
    // 把按钮移到最上层(假设你给按钮加了IBOutlet:startBtn和stopBtn)
    if let startBtn = startBtn, let stopBtn = stopBtn {
        view.bringSubviewToFront(startBtn)
        view.bringSubviewToFront(stopBtn)
    }
}

2. 修改AppDelegate的startUnity方法,阻止Unity抢占窗口层级

Unity的默认启动逻辑会直接接管窗口,我们要修改它,让Unity视图作为当前ViewController的子视图存在:

func startUnity() {
    guard !isUnityRunning else { return }
    isUnityRunning = true
    
    // 初始化Unity但不让它自动接管窗口
    if currentUnityController.window == nil {
        currentUnityController.window = UIWindow()
    }
    currentUnityController.startUnity()
    
    // 延迟一小段时间,确保Unity视图已经创建,再把它移到当前ViewController的视图中
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
        guard let self = self, 
              let unityView = UnityGetGLView(),
              let currentVC = self.window?.rootViewController as? Unity3DViewController else {
            return
        }
        unityView.removeFromSuperview()
        currentVC.view.addSubview(unityView)
        // 再次确保按钮在顶层
        if let startBtn = currentVC.startBtn, let stopBtn = currentVC.stopBtn {
            currentVC.view.bringSubviewToFront(startBtn)
            currentVC.view.bringSubviewToFront(stopBtn)
        }
    }
}

3. 完善stopUnity方法,移除Unity视图

停止Unity时,不仅要触发生命周期方法,还要把Unity视图从界面上移除:

func stopUnity() {
    guard isUnityRunning else { return }
    currentUnityController.applicationWillResignActive(application!)
    // 移除Unity视图,让按钮重新显示
    UnityGetGLView()?.removeFromSuperview()
    isUnityRunning = false
}

4. 额外保障:监听视图层级变化,强制按钮在顶层

有些Unity版本会在运行时偷偷把自己的视图移到最上层,我们可以加个监听,确保按钮始终在顶层:

在Unity3DViewController的viewDidLoad中添加:

override func viewDidLoad() {
    super.viewDidLoad()
    // 监听窗口可见事件,每次窗口刷新都把按钮移到最前
    NotificationCenter.default.addObserver(self, 
                                           selector: #selector(bringButtonsToFront), 
                                           name: UIWindow.didBecomeVisibleNotification, 
                                           object: nil)
}

@objc private func bringButtonsToFront() {
    if let startBtn = startBtn, let stopBtn = stopBtn {
        view.bringSubviewToFront(startBtn)
        view.bringSubviewToFront(stopBtn)
    }
}

// 记得在销毁时移除监听
deinit {
    NotificationCenter.default.removeObserver(self)
}

这样修改后,你的按钮就会始终显示在Unity视图的上层,不会被遮挡了。

内容的提问来源于stack exchange,提问作者Vikas Roy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:44:52