112 lines
3.7 KiB
Swift
112 lines
3.7 KiB
Swift
//
|
||
// TabNavigationController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import UIKit
|
||
|
||
@MainActor
|
||
/// 单个 Tab 的导航栈,与 `AppRouter` 路径同步并在 push 时隐藏底部 TabBar。
|
||
final class TabNavigationController: UINavigationController, UINavigationControllerDelegate {
|
||
|
||
let appTab: AppTab
|
||
private let services: AppServices
|
||
private var isSyncingStack = false
|
||
|
||
weak var tabBarHost: MainTabBarController?
|
||
|
||
init(tab: AppTab, services: AppServices) {
|
||
self.appTab = tab
|
||
self.services = services
|
||
super.init(nibName: nil, bundle: nil)
|
||
delegate = self
|
||
navigationBar.prefersLargeTitles = false
|
||
setViewControllers([tab.makeRootViewController(services: services)], animated: false)
|
||
registerNavigationBridge()
|
||
services.appRouter.router(for: appTab).onChange = { [weak self] in
|
||
self?.syncFromRouterPath()
|
||
}
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
/// Push 指定应用路由,并同步写入 RouterPath。
|
||
func pushRoute(_ route: AppRoute, animated: Bool = true) {
|
||
let viewController = AppRouteViewControllerFactory.makeViewController(for: route, services: services)
|
||
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
|
||
isSyncingStack = true
|
||
services.appRouter.router(for: appTab).navigate(to: route)
|
||
isSyncingStack = false
|
||
pushViewController(viewController, animated: animated)
|
||
}
|
||
|
||
func navigationController(
|
||
_ navigationController: UINavigationController,
|
||
willShow viewController: UIViewController,
|
||
animated: Bool
|
||
) {
|
||
let isRoot = viewController === viewControllers.first
|
||
tabBarHost?.setCustomTabBarHidden(!isRoot, animated: animated)
|
||
}
|
||
|
||
func navigationController(
|
||
_ navigationController: UINavigationController,
|
||
didShow viewController: UIViewController,
|
||
animated: Bool
|
||
) {
|
||
syncRouterPathFromStack()
|
||
}
|
||
|
||
private func registerNavigationBridge() {
|
||
switch appTab {
|
||
case .home:
|
||
UIKitAppNavigation.homeNavigationController = self
|
||
case .profile:
|
||
UIKitAppNavigation.profileNavigationController = self
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
|
||
private func syncFromRouterPath() {
|
||
guard !isSyncingStack else { return }
|
||
|
||
let routerPath = services.appRouter.router(for: appTab)
|
||
let targetDepth = routerPath.path.count
|
||
let currentDepth = max(viewControllers.count - 1, 0)
|
||
|
||
if targetDepth > currentDepth {
|
||
for index in currentDepth..<targetDepth {
|
||
let route = routerPath.path[index]
|
||
let viewController = AppRouteViewControllerFactory.makeViewController(for: route, services: services)
|
||
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
|
||
pushViewController(viewController, animated: true)
|
||
}
|
||
return
|
||
}
|
||
|
||
if targetDepth < currentDepth {
|
||
if targetDepth == 0 {
|
||
popToRootViewController(animated: true)
|
||
} else if targetDepth < viewControllers.count {
|
||
popToViewController(viewControllers[targetDepth], animated: true)
|
||
}
|
||
}
|
||
}
|
||
|
||
private func syncRouterPathFromStack() {
|
||
guard !isSyncingStack else { return }
|
||
|
||
let routerPath = services.appRouter.router(for: appTab)
|
||
let depth = max(viewControllers.count - 1, 0)
|
||
if depth < routerPath.path.count {
|
||
isSyncingStack = true
|
||
routerPath.path = Array(routerPath.path.prefix(depth))
|
||
isSyncingStack = false
|
||
}
|
||
}
|
||
}
|