ios26+ 液态玻璃UI 适配

禁用液态玻璃UI 的key 如果是27版本构建 会自动忽略
Apple Developer Documentation

自定义的Tabbar 通过 [self setValue:customTabBar forKey:@"tabBar"];设置的 也会失效或者出现各种问题

iOS 26 的 TabBar 是悬浮式设计,页面侧滑返回时,如果 TabBar 的显示状态与导航栈不一致,就可能出现 TabBar 提前显示、位置跳动或动画不协调的问题。 这需要结合 UITabBarController 和 UINavigationController 的层级来处理.

目前大多主流的层级是

1
2
3
4
5
6
7
8
9
10
11
UITabBarController
├── BaseNavigationController(首页导航栈)
│ └── HomeViewController(首页)
│ └── DetailViewController(详情)
│
├── BaseNavigationController(商城导航栈)
│ └── MallViewController(商城)
│ └── DetailViewController(详情)
│
└── BaseNavigationController(我的导航栈)
└── MineViewController(我的)

这种 系统的Tabbar
如果是 push时不隐藏 没有问题
如果是 push时隐藏 hidesBottomBarWhenPushed = true 在侧滑的时候会提前出现 停止侧滑 又会隐藏

网上有几种方案处理:

方案一:自定义导航控制器转场代理

通过实现UINavigationControllerDelegate手动控制转场过程中的TabBar层级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .pop {
return CustomPopTransition()
}
return nil
}
}

class CustomPopTransition: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.35
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) else {
transitionContext.completeTransition(false)
return
}

let containerView = transitionContext.containerView
containerView.addSubview(toVC.view)
containerView.sendSubviewToBack(toVC.view)

fromVC.view.frame = containerView.bounds
let finalFrame = CGRect(x: containerView.bounds.width, y: 0, width: containerView.bounds.width, height: containerView.bounds.height)

UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
fromVC.view.frame = finalFrame
if let tabBar = (toVC as? UITabBarController)?.tabBar {
containerView.sendSubviewToBack(tabBar)
}
} _ in
fromVC.view.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}

方案二:监听侧滑手势动态调整TabBar层级

在详情页监听导航控制器的侧滑手势,根据手势状态调整TabBar层级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class DetailViewController: UIViewController {
private var tabBar: UITabBar?

override func viewDidLoad() {
super.viewDidLoad()
tabBar = tabBarController?.tabBar

if let popGesture = navigationController?.interactivePopGestureRecognizer {
popGesture.addTarget(self, action: #selector(handlePopGesture(_:)))
}
}

@objc private func handlePopGesture(_ gesture: UIPanGestureRecognizer) {
guard let tabBar = tabBar else { return }

switch gesture.state {
case .began, .changed:
tabBar.superview?.sendSubviewToBack(tabBar)
case .cancelled:
tabBar.superview?.bringSubviewToFront(tabBar)
default:
break
}
}
}

方案三:修改TabBar的zPosition(简洁版)

在详情页生命周期方法中直接调整TabBar的层级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DetailViewController: UIViewController {
private var originalZPosition: CGFloat = 0

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let tabBar = tabBarController?.tabBar {
originalZPosition = tabBar.layer.zPosition
tabBar.layer.zPosition = -1000
}
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let tabBar = tabBarController?.tabBar {
tabBar.layer.zPosition = originalZPosition
}
}
}

方案四:修改Tabbar的层级(推荐)

TabBarController外面包一层UINavigationController 所有跳转 统一处理 比如 TheRouter 仓库
TheRouter 官网

1
2
3
4
5
6
7
8
9
10
11
12
UINavigationController
└─ UITabBarController
├── BaseNavigationController(首页导航栈)
│ └── HomeViewController(首页)
│ └── DetailViewController(详情)
│
├── BaseNavigationController(商城导航栈)
│ └── MallViewController(商城)
│ └── DetailViewController(详情)
│
└── BaseNavigationController(我的导航栈)
└── MineViewController(我的)

并禁止相关属性 设置为window.rootViewController

1
2
3
4
5
6
7
8
// 隐藏外层导航栏
nav.navigationBarHidden = YES;
// 禁止外层系统侧滑返回手势
nav.interactivePopGestureRecognizer.enabled = NO;
// 禁止通过导航栏边缘手势触发交互式 Pop
nav.interactivePopGestureRecognizer.delegate = nil;
// 作为window根控制器
window.rootViewController = nav;

注意
跳转子页面 走路由统一跳转 不能直接navigationController.pushViewController 去跳转 不然会失效