Rename tab icons to snake_case with @2x/@3x scales, register CYL plus button at app launch, and remove redundant Podfile hooks already covered by the Xcode project. Co-authored-by: Cursor <cursoragent@cursor.com>
201 lines
7.2 KiB
Swift
201 lines
7.2 KiB
Swift
//
|
||
// MainTabBarController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import CYLTabBarController
|
||
import UIKit
|
||
|
||
@MainActor
|
||
/// 主 Tab 容器,使用 CYLTabBarController 展示四个一级入口及中间扫码按钮。
|
||
final class MainTabBarController: CYLTabBarController {
|
||
|
||
private let services: AppServices
|
||
private let badgeViewModel = MainTabBadgeViewModel()
|
||
private var tabNavigationControllers: [AppTab: TabNavigationController] = [:]
|
||
private var isSyncingTabSelection = false
|
||
|
||
/// 初始化实例。
|
||
init(services: AppServices) {
|
||
self.services = services
|
||
super.init(nibName: nil, bundle: nil)
|
||
MainScanPlusButton.onScanTap = { [weak self] in
|
||
self?.presentGlobalScanner()
|
||
}
|
||
// delegate = self
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
/// 视图加载完成后的 UI 初始化与数据绑定。
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
configureTabBarAppearance()
|
||
configureTabs()
|
||
services.appNavigator.attach(host: self)
|
||
bindBadgeViewModel()
|
||
bindAccountContext()
|
||
Task { await refreshOrderBadge() }
|
||
#if DEBUG
|
||
Task { await AppUITestRouteDriver.applyIfNeeded(services: services) }
|
||
#endif
|
||
}
|
||
|
||
/// 控制器释放前清理全局扫码按钮回调。
|
||
deinit {
|
||
Task { @MainActor in
|
||
MainScanPlusButton.onScanTap = nil
|
||
}
|
||
}
|
||
|
||
/// 配置 TabBar 外观,对齐参考工程 CustomMainTabBar 样式。
|
||
private func configureTabBarAppearance() {
|
||
tabBar.tintColor = AppDesign.primary
|
||
tabBar.backgroundColor = .white
|
||
tabBar.shadowImage = UIImage()
|
||
|
||
let normalAttributes: [NSAttributedString.Key: Any] = [
|
||
.foregroundColor: UIColor(hex: 0x7D8DA3),
|
||
.font: UIFont.systemFont(ofSize: 12, weight: .regular)
|
||
]
|
||
let selectedAttributes: [NSAttributedString.Key: Any] = [
|
||
.foregroundColor: AppDesign.primary,
|
||
.font: UIFont.systemFont(ofSize: 12, weight: .medium)
|
||
]
|
||
|
||
let appearance = UITabBarAppearance()
|
||
appearance.configureWithOpaqueBackground()
|
||
appearance.backgroundColor = .white
|
||
appearance.shadowColor = .clear
|
||
appearance.stackedLayoutAppearance.normal.titleTextAttributes = normalAttributes
|
||
appearance.stackedLayoutAppearance.selected.titleTextAttributes = selectedAttributes
|
||
tabBar.standardAppearance = appearance
|
||
tabBar.scrollEdgeAppearance = appearance
|
||
}
|
||
|
||
/// 为每个 Tab 创建独立导航栈,并按 CYLTabBarController 要求先配置 Tab 属性再设置控制器。
|
||
private func configureTabs() {
|
||
let tabs = AppTab.allCases
|
||
tabBarItemsAttributes = tabs.map { makeTabBarItemAttributes(for: $0) }
|
||
viewControllers = tabs.map { tab in
|
||
let navigationController = TabNavigationController(tab: tab, services: services)
|
||
tabNavigationControllers[tab] = navigationController
|
||
return navigationController
|
||
}
|
||
}
|
||
|
||
/// 构建 CYLTabBarController 识别的 TabBarItem 属性字典。
|
||
private func makeTabBarItemAttributes(for tab: AppTab) -> [String: String] {
|
||
[
|
||
CYLTabBarItemTitle: tab.title,
|
||
CYLTabBarItemImage: tab.unselectedImageName,
|
||
CYLTabBarItemSelectedImage: tab.selectedImageName
|
||
]
|
||
}
|
||
|
||
/// 绑定角标 ViewModel 回调。
|
||
private func bindBadgeViewModel() {
|
||
badgeViewModel.onChange = { [weak self] in
|
||
self?.updateOrderBadge()
|
||
}
|
||
}
|
||
|
||
/// 绑定账号上下文,景区或门店变化时刷新角标。
|
||
private func bindAccountContext() {
|
||
services.accountContext.onChange = { [weak self] in
|
||
Task { await self?.refreshOrderBadge() }
|
||
}
|
||
}
|
||
|
||
/// 更新订单 Tab 待核销角标。
|
||
private func updateOrderBadge() {
|
||
let badgeValue: String?
|
||
if let count = badgeViewModel.pendingWriteOffCount, count > 0 {
|
||
badgeValue = "\(count)"
|
||
} else {
|
||
badgeValue = nil
|
||
}
|
||
tabNavigationControllers[.orders]?.tabBarItem.badgeValue = badgeValue
|
||
}
|
||
|
||
/// 刷新订单 Tab 待核销角标数据。
|
||
private func refreshOrderBadge() async {
|
||
await badgeViewModel.refreshPendingWriteOffCount(
|
||
api: services.ordersAPI,
|
||
scenicId: services.accountContext.currentScenic?.id,
|
||
storeId: services.accountContext.currentStore?.id
|
||
)
|
||
}
|
||
|
||
/// 从 TabBar 中间按钮打开全局扫码页,成功后路由至订单核销入口。
|
||
private func presentGlobalScanner() {
|
||
let scanner = OrderCodeScannerViewController()
|
||
scanner.onScanResult = { [weak self, weak scanner] result in
|
||
scanner?.dismiss(animated: true)
|
||
guard let self else { return }
|
||
switch result {
|
||
case .success(let code):
|
||
self.services.appNavigator.openOrdersEntry(.verificationOrders, scannedCode: code)
|
||
case .failure(let error):
|
||
self.services.toastCenter.show(error.localizedDescription)
|
||
}
|
||
}
|
||
let navigationController = UINavigationController(rootViewController: scanner)
|
||
navigationController.modalPresentationStyle = .fullScreen
|
||
present(navigationController, animated: true)
|
||
}
|
||
|
||
/// 用户点击 TabBar 时刷新订单角标。
|
||
override func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
|
||
guard !isSyncingTabSelection,
|
||
let navigationController = viewController as? TabNavigationController else { return }
|
||
|
||
if navigationController.appTab == .orders {
|
||
Task { await refreshOrderBadge() }
|
||
}
|
||
}
|
||
}
|
||
|
||
extension MainTabBarController: AppNavigationHost {
|
||
/// 选中指定主 Tab。
|
||
func selectTab(_ tab: AppTab) {
|
||
guard let index = AppTab.allCases.firstIndex(of: tab) else { return }
|
||
guard selectedIndex != index else { return }
|
||
isSyncingTabSelection = true
|
||
selectedIndex = index
|
||
isSyncingTabSelection = false
|
||
if tab == .orders {
|
||
Task { await refreshOrderBadge() }
|
||
}
|
||
}
|
||
|
||
/// 返回指定 Tab 的导航控制器。
|
||
func navigationController(for tab: AppTab) -> TabNavigationController? {
|
||
tabNavigationControllers[tab]
|
||
}
|
||
|
||
/// 返回当前选中 Tab 的导航控制器。
|
||
func selectedNavigationController() -> TabNavigationController? {
|
||
selectedViewController as? TabNavigationController
|
||
}
|
||
|
||
/// 重置全部 Tab 的导航栈。
|
||
func resetAllStacks() {
|
||
for navigationController in tabNavigationControllers.values {
|
||
navigationController.popToRootViewController(animated: false)
|
||
}
|
||
selectTab(.home)
|
||
}
|
||
|
||
/// 将订单入口和可选扫码结果应用到订单根页面。
|
||
func applyOrdersEntry(_ entry: OrdersEntry, scannedCode: String?) {
|
||
guard let ordersController = tabNavigationControllers[.orders]?.viewControllers.first as? OrdersViewController else {
|
||
return
|
||
}
|
||
ordersController.applyOrdersEntry(entry, scannedCode: scannedCode)
|
||
}
|
||
}
|