Files
suixinkan_ios_new/suixinkan/Features/Main/Views/CustomMainTabBar.swift
汉秋 79e735f628 为 TabBar 和扫码页补充 accessibilityIdentifier,并在模拟器/UI Test 中禁用真实相机采集。
预初始化各 Tab 路由容器,修正 iOS 17 ContentUnavailableView 调用,提升自定义 TabBar 与扫码流程的 UI Test 稳定性。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 09:34:09 +08:00

158 lines
4.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// CustomMainTabBar.swift
// suixinkan
//
// Created by Codex on 2026/6/23.
//
import SwiftUI
/// TabBar
struct CustomMainTabBar: View {
@Binding var selectedTab: AppTab
let orderBadgeText: String?
let onScanTap: () -> Void
private let items = CustomMainTabItem.defaultItems
var body: some View {
HStack(alignment: .center, spacing: 0) {
CustomMainTabBarItem(
item: items[0],
selectedTab: $selectedTab,
badgeText: nil
)
CustomMainTabBarItem(
item: items[1],
selectedTab: $selectedTab,
badgeText: orderBadgeText
)
CustomMainTabBarCenterAction(onScanTap: onScanTap)
.frame(width: 74)
CustomMainTabBarItem(
item: items[2],
selectedTab: $selectedTab,
badgeText: nil
)
CustomMainTabBarItem(
item: items[3],
selectedTab: $selectedTab,
badgeText: nil
)
}
.frame(maxWidth: .infinity)
.frame(height: 72)
.padding(.horizontal, 22)
.background(.white)
.overlay(alignment: .top) {
Rectangle()
.fill(Color.black.opacity(0.04))
.frame(height: 0.5)
}
}
}
/// TabBar
private struct CustomMainTabItem {
let tab: AppTab
let title: String
let selectedImage: String
let unselectedImage: String
static let defaultItems: [CustomMainTabItem] = [
.init(tab: .home, title: "首页", selectedImage: "TabHomeSelected", unselectedImage: "TabHomeUnselected"),
.init(tab: .orders, title: "订单", selectedImage: "TabOrderSelected", unselectedImage: "TabOrderUnselected"),
.init(tab: .statistics, title: "数据", selectedImage: "TabDataSelected", unselectedImage: "TabDataUnselected"),
.init(tab: .profile, title: "我的", selectedImage: "TabProfileSelected", unselectedImage: "TabProfileUnselected")
]
}
/// TabBar
private struct CustomMainTabBarItem: View {
let item: CustomMainTabItem
@Binding var selectedTab: AppTab
let badgeText: String?
private var isSelected: Bool {
selectedTab == item.tab
}
var body: some View {
Button {
selectedTab = item.tab
} label: {
VStack(spacing: 3) {
ZStack(alignment: .topTrailing) {
Image(isSelected ? item.selectedImage : item.unselectedImage)
.renderingMode(.original)
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
if let badgeText {
CustomMainTabBadge(text: badgeText)
}
}
Text(item.title)
.font(.system(size: 12, weight: isSelected ? .medium : .regular))
.foregroundStyle(isSelected ? AppDesign.primary : Color(hex: 0x7D8DA3))
.lineLimit(1)
.minimumScaleFactor(0.8)
}
.frame(maxWidth: .infinity)
.frame(height: 58)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.accessibilityLabel(item.title)
.accessibilityIdentifier("main.tab.\(item.tab.rawValue)")
}
}
/// TabBar
private struct CustomMainTabBarCenterAction: View {
let onScanTap: () -> Void
var body: some View {
Button(action: onScanTap) {
ZStack {
Circle()
.fill(AppDesign.primary)
.frame(width: 58, height: 58)
Image(systemName: "qrcode.viewfinder")
.font(.system(size: 30, weight: .bold))
.foregroundStyle(.white)
}
.frame(width: 70, height: 64)
.contentShape(Circle())
}
.buttonStyle(.plain)
.accessibilityLabel("扫码核销")
.accessibilityIdentifier("main.scan")
}
}
/// TabBar
private struct CustomMainTabBadge: View {
let text: String
var body: some View {
Text(text)
.font(.system(size: 9, weight: .bold))
.foregroundStyle(.white)
.frame(minWidth: 15, minHeight: 15)
.padding(.horizontal, 2)
.background(Color(hex: 0xEF4444), in: Capsule())
.offset(x: 9, y: -7)
}
}
#Preview {
CustomMainTabBar(
selectedTab: .constant(.orders),
orderBadgeText: "8",
onScanTap: {}
)
}