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
相关产品推荐
相关产品推荐

