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

iOS开发:如何让TabbarView.xib全局可见且不遮挡下层控件交互?

问题:全局TabbarView保持可见且不遮挡底层交互

我在第一个ViewController中加载了TabbarView.xib(黄色矩形),切换到第二个ViewController时该视图会消失,希望它处于更高层级,在所有界面中始终保持可见。

尝试用windowNew?.windowLevel = .alert实现全局可见,但gotoVC2按钮因被windowNew全屏覆盖无法点击,需要在保持TabbarView全局可见的同时,让底层按钮正常交互。

现有代码

第一个ViewController代码

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var gotoVC: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        gotoVC.addTarget(self, action: #selector(gotoVCAction), for: .touchUpInside)
        
    }
    
    @objc func gotoVCAction() {
        performSegue(withIdentifier: "gotoVC2", sender: nil)
    }
}

TabbarView.xib对应代码

import UIKit

class TabbarView: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        print("TabbarView")
    }
}

Scene Delegate代码

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

var windowNew: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    
    guard let windowScene = (scene as? UIWindowScene) else { return }
    
    windowNew = UIWindow(windowScene: windowScene)
    
    let rootViewController = TabbarView()
    
    windowNew?.windowLevel = .alert
    
    windowNew?.rootViewController = rootViewController
    
    windowNew?.makeKeyAndVisible()
    
}

解决方案

思路1:缩小全局窗口至Tabbar区域

让承载TabbarView的窗口仅覆盖Tabbar所在的底部区域,而非全屏,同时设置正确的层级确保可见:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?
    var windowNew: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        
        // 初始化并显示主窗口
        window = UIWindow(windowScene: windowScene)
        let mainVC = ViewController()
        window?.rootViewController = mainVC
        window?.makeKeyAndVisible()
        
        // 初始化Tabbar专属窗口
        windowNew = UIWindow(windowScene: windowScene)
        let tabbarVC = TabbarView()
        windowNew?.rootViewController = tabbarVC
        
        // 设置窗口层级高于主窗口,确保Tabbar始终在顶层
        windowNew?.windowLevel = .normal + 1
        
        // 固定窗口为底部Tabbar区域(适配安全区)
        let safeBottomInset = windowScene.safeAreaInsets.bottom
        let tabbarHeight: CGFloat = 83
        windowNew?.frame = CGRect(
            x: 0,
            y: windowScene.screen.bounds.height - tabbarHeight - safeBottomInset,
            width: windowScene.screen.bounds.width,
            height: tabbarHeight + safeBottomInset
        )
        
        // 启用窗口交互,仅Tabbar区域可响应点击
        windowNew?.isUserInteractionEnabled = true
        windowNew?.makeKeyAndVisible()
    }
}

同时需在TabbarView的xib中设置视图约束:底部对齐安全区、左右铺满、高度固定为83pt,确保视图与窗口区域匹配。


思路2:通过hitTest实现点击穿透

如果不想调整窗口大小,可在TabbarView中重写hitTest方法,仅处理Tabbar区域的点击事件,其余区域让事件穿透到底层窗口:

class TabbarView: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        // 定义Tabbar的交互区域(根据实际布局调整)
        let safeBottomInset = view.window?.windowScene?.safeAreaInsets.bottom ?? 0
        let tabbarInteractiveFrame = CGRect(
            x: 0,
            y: view.bounds.height - 83 - safeBottomInset,
            width: view.bounds.width,
            height: 83 + safeBottomInset
        )
        
        // 仅当点击点在Tabbar区域内时,返回响应视图;否则返回nil让事件穿透
        return tabbarInteractiveFrame.contains(point) ? hitView : nil
    }
}

同时保持SceneDelegate中窗口层级设置,但无需调整窗口frame:

windowNew?.windowLevel = .normal + 1 // 避免使用.alert层级,防止覆盖系统弹窗

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 18:22:45