iOS原生集成Unity视图层级问题:按钮被Unity视图遮挡
解决Unity视图遮挡原生按钮的问题
看起来你遇到的核心问题是Unity启动后会自动把自己的视图放到整个窗口的最顶层,直接覆盖了你的原生按钮。咱们一步步来分析和解决:
问题根源
- Unity的视图层级抢占:你调用
appDelegate.startUnity()时,内部的applicationDidBecomeActive方法会触发Unity的初始化逻辑,它会自动将Unity视图添加到UIWindow的最上层,完全绕过了你在showUnitySubView里把视图插入到at:0的操作——毕竟窗口层级比视图层级优先级更高。 - 停止逻辑不完整:你的
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
相关产品推荐
相关产品推荐

