Fix home navigation bar visibility and hide scan tab title.
Home uses a custom top bar, so TabNavigationController now syncs system nav bar visibility with the stack top; remove the scan tab text label to show icon only. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -112,4 +112,6 @@ Toast 展示为顶部全宽横幅,背景使用不透明主色并延伸到屏
|
|||||||
|
|
||||||
Tab 根页面显示底部 TabBar。通过 `AppRoute` push 到子页面时,会根据 `AppRoute.hidesTabBarWhenPushed` 统一隐藏 TabBar。
|
Tab 根页面显示底部 TabBar。通过 `AppRoute` push 到子页面时,会根据 `AppRoute.hidesTabBarWhenPushed` 统一隐藏 TabBar。
|
||||||
|
|
||||||
|
首页根页面使用自定义顶部栏,因此 `TabNavigationController` 会在展示 `HomeViewController` 时隐藏系统导航栏;push 到其他业务子页面或从子页面返回时,由导航栈统一按栈顶页面恢复系统导航栏显隐,避免首页隐藏状态影响子页面标题栏和返回按钮。
|
||||||
|
|
||||||
跨 Tab、推送通知、UI Test 直达和全局扫码都通过 `AppNavigator` 处理。`AppNavigationPolicy.currentStack` 沿当前栈 push,`.tabRoot` 切到目标 Tab 并先回到 root,`.preserveTab` 只切换 Tab 并保留原栈。新增页面时优先扩展 `AppRoute` / `HomeRoute` / `ProfileRoute` / `OrdersRoute`,并在 `AppRouteViewControllerFactory` 注册对应 ViewController。
|
跨 Tab、推送通知、UI Test 直达和全局扫码都通过 `AppNavigator` 处理。`AppNavigationPolicy.currentStack` 沿当前栈 push,`.tabRoot` 切到目标 Tab 并先回到 root,`.preserveTab` 只切换 Tab 并保留原栈。新增页面时优先扩展 `AppRoute` / `HomeRoute` / `ProfileRoute` / `OrdersRoute`,并在 `AppRouteViewControllerFactory` 注册对应 ViewController。
|
||||||
|
|||||||
@ -17,6 +17,7 @@ final class TabNavigationController: UINavigationController {
|
|||||||
self.appTab = tab
|
self.appTab = tab
|
||||||
self.services = services
|
self.services = services
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
delegate = self
|
||||||
navigationBar.prefersLargeTitles = false
|
navigationBar.prefersLargeTitles = false
|
||||||
setViewControllers([tab.makeRootViewController(services: services)], animated: false)
|
setViewControllers([tab.makeRootViewController(services: services)], animated: false)
|
||||||
}
|
}
|
||||||
@ -32,4 +33,66 @@ final class TabNavigationController: UINavigationController {
|
|||||||
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
|
viewController.hidesBottomBarWhenPushed = route.hidesTabBarWhenPushed
|
||||||
pushViewController(viewController, animated: animated)
|
pushViewController(viewController, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push 页面时按目标页同步系统导航栏状态,覆盖各页面直接调用系统 push 的场景。
|
||||||
|
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
|
||||||
|
updateNavigationBarVisibility(for: viewController, animated: animated)
|
||||||
|
super.pushViewController(viewController, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 设置导航栈时按最终栈顶同步系统导航栏状态。
|
||||||
|
override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
|
||||||
|
if let topViewController = viewControllers.last {
|
||||||
|
updateNavigationBarVisibility(for: topViewController, animated: animated)
|
||||||
|
}
|
||||||
|
super.setViewControllers(viewControllers, animated: animated)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 弹出页面后按新的栈顶同步系统导航栏状态。
|
||||||
|
override func popViewController(animated: Bool) -> UIViewController? {
|
||||||
|
let poppedViewController = super.popViewController(animated: animated)
|
||||||
|
if !animated, let topViewController {
|
||||||
|
updateNavigationBarVisibility(for: topViewController, animated: false)
|
||||||
|
}
|
||||||
|
return poppedViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 回到根页面后按根页面同步系统导航栏状态。
|
||||||
|
override func popToRootViewController(animated: Bool) -> [UIViewController]? {
|
||||||
|
let poppedViewControllers = super.popToRootViewController(animated: animated)
|
||||||
|
if !animated, let topViewController {
|
||||||
|
updateNavigationBarVisibility(for: topViewController, animated: false)
|
||||||
|
}
|
||||||
|
return poppedViewControllers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 弹到指定页面后按该页面同步系统导航栏状态。
|
||||||
|
override func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
|
||||||
|
let poppedViewControllers = super.popToViewController(viewController, animated: animated)
|
||||||
|
if !animated, let topViewController {
|
||||||
|
updateNavigationBarVisibility(for: topViewController, animated: false)
|
||||||
|
}
|
||||||
|
return poppedViewControllers
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 根据页面类型判断系统导航栏是否应隐藏。
|
||||||
|
private func shouldHideNavigationBar(for viewController: UIViewController) -> Bool {
|
||||||
|
viewController is HomeViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将系统导航栏显示状态同步到目标页面,避免首页隐藏状态泄漏到子页面。
|
||||||
|
private func updateNavigationBarVisibility(for viewController: UIViewController, animated: Bool) {
|
||||||
|
setNavigationBarHidden(shouldHideNavigationBar(for: viewController), animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TabNavigationController: UINavigationControllerDelegate {
|
||||||
|
/// 导航转场即将展示页面时更新系统导航栏显隐状态。
|
||||||
|
func navigationController(
|
||||||
|
_ navigationController: UINavigationController,
|
||||||
|
willShow viewController: UIViewController,
|
||||||
|
animated: Bool
|
||||||
|
) {
|
||||||
|
updateNavigationBarVisibility(for: viewController, animated: animated)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ enum MainScanTabItem {
|
|||||||
static func makeTabBarItem() -> UITabBarItem {
|
static func makeTabBarItem() -> UITabBarItem {
|
||||||
let image = UIImage(named: "icon_scan")?.withRenderingMode(.alwaysOriginal)
|
let image = UIImage(named: "icon_scan")?.withRenderingMode(.alwaysOriginal)
|
||||||
let selectedImage = UIImage(named: "icon_scan")?.withRenderingMode(.alwaysOriginal)
|
let selectedImage = UIImage(named: "icon_scan")?.withRenderingMode(.alwaysOriginal)
|
||||||
let item = UITabBarItem(title: "扫码", image: image, selectedImage: selectedImage)
|
let item = UITabBarItem(title: nil, image: image, selectedImage: selectedImage)
|
||||||
item.accessibilityLabel = "扫码核销"
|
item.accessibilityLabel = "扫码核销"
|
||||||
item.accessibilityIdentifier = "main.scan"
|
item.accessibilityIdentifier = "main.scan"
|
||||||
return item
|
return item
|
||||||
|
|||||||
@ -35,7 +35,7 @@ final class MainScanTabItemTests: XCTestCase {
|
|||||||
controller.loadViewIfNeeded()
|
controller.loadViewIfNeeded()
|
||||||
|
|
||||||
let scanItem = try XCTUnwrap(controller.viewControllers?[2].tabBarItem)
|
let scanItem = try XCTUnwrap(controller.viewControllers?[2].tabBarItem)
|
||||||
XCTAssertEqual(scanItem.title, "扫码")
|
XCTAssertNil(scanItem.title)
|
||||||
XCTAssertEqual(scanItem.accessibilityIdentifier, "main.scan")
|
XCTAssertEqual(scanItem.accessibilityIdentifier, "main.scan")
|
||||||
XCTAssertEqual(scanItem.accessibilityLabel, "扫码核销")
|
XCTAssertEqual(scanItem.accessibilityLabel, "扫码核销")
|
||||||
XCTAssertNotNil(scanItem.image)
|
XCTAssertNotNil(scanItem.image)
|
||||||
|
|||||||
@ -27,6 +27,20 @@ final class AppNavigatorTests: XCTestCase {
|
|||||||
XCTAssertTrue(homeStack.viewControllers.last is PaymentCollectionViewController)
|
XCTAssertTrue(homeStack.viewControllers.last is PaymentCollectionViewController)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 测试首页隐藏系统导航栏,push 子页面后显示,返回首页后重新隐藏。
|
||||||
|
func testHomeNavigationBarVisibilityFollowsTopViewController() {
|
||||||
|
let services = AppServices(apiClient: APIClient(session: AppNavigatorRecordingURLSession()))
|
||||||
|
let navigationController = TabNavigationController(tab: .home, services: services)
|
||||||
|
|
||||||
|
XCTAssertTrue(navigationController.isNavigationBarHidden)
|
||||||
|
|
||||||
|
navigationController.push(route: .home(.paymentCollection), animated: false)
|
||||||
|
XCTAssertFalse(navigationController.isNavigationBarHidden)
|
||||||
|
|
||||||
|
navigationController.popToRootViewController(animated: false)
|
||||||
|
XCTAssertTrue(navigationController.isNavigationBarHidden)
|
||||||
|
}
|
||||||
|
|
||||||
/// 测试 tabRoot 会切换到指定 Tab、清空旧栈并打开目标页。
|
/// 测试 tabRoot 会切换到指定 Tab、清空旧栈并打开目标页。
|
||||||
func testTabRootSelectsTabResetsStackAndPushesRoute() {
|
func testTabRootSelectsTabResetsStackAndPushesRoute() {
|
||||||
let services = AppServices(apiClient: APIClient(session: AppNavigatorRecordingURLSession()))
|
let services = AppServices(apiClient: APIClient(session: AppNavigatorRecordingURLSession()))
|
||||||
|
|||||||
Reference in New Issue
Block a user