导入 XuXuCheck 工程(iOS 干化学尿液试纸分析 App)
从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。 为控制仓库体积做了两处删减: - 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片 (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该 xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录, 只保留 ios-arm64 与 ios-arm64_x86_64-simulator。 - 移除全部 .DS_Store 与 xcuserdata/。 已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED, ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号 (OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。 仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// TabBar.swift
|
||||
// XuXuCheck
|
||||
//
|
||||
// Created by henseycai on 2025/6/11.
|
||||
//
|
||||
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct TabBar: View {
|
||||
@AppStorage("selectedTab") var selectedTab: Tab = .home
|
||||
@State var color : Color = .teal
|
||||
@State var tabItemWidth: CGFloat = 0
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .bottom) {
|
||||
GeometryReader { proxy in
|
||||
let hasHomeIndicator: Bool = proxy.safeAreaInsets.bottom > 20
|
||||
|
||||
HStack {
|
||||
buttons
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.top, 14)
|
||||
.frame(height: hasHomeIndicator ? 88 : 62, alignment: .top)
|
||||
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: hasHomeIndicator ? 34 : 0, style: .continuous))
|
||||
.background(
|
||||
background
|
||||
)
|
||||
.overlay(
|
||||
overlay
|
||||
)
|
||||
.strokeStyle(cornerRadius: hasHomeIndicator ? 34 : 0)
|
||||
.frame(maxHeight: .infinity, alignment: .bottom)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buttons: some View {
|
||||
ForEach(tabItems) { item in
|
||||
Button {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
|
||||
selectedTab = item.tab
|
||||
color = item.color
|
||||
}
|
||||
} label: {
|
||||
VStack(spacing: 0) {
|
||||
Image(systemName: item.icon)
|
||||
.symbolVariant(.fill)
|
||||
.font(.body.bold())
|
||||
.frame(width: 44, height: 29)
|
||||
Text(item.text)
|
||||
.font(.caption2)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.foregroundStyle(selectedTab == item.tab ? .primary : .secondary)
|
||||
.blendMode(selectedTab == item.tab ? .overlay : .normal)
|
||||
.overlay(
|
||||
GeometryReader { proxy in
|
||||
Color.clear.preference(key: TabPreferenceKey.self, value: proxy.size.width)
|
||||
}
|
||||
)
|
||||
.onPreferenceChange(TabPreferenceKey.self) { value in
|
||||
tabItemWidth = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var background : some View {
|
||||
HStack {
|
||||
if selectedTab == .mine { Spacer() }
|
||||
if selectedTab == .members {
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .records {
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
Circle().fill(color).frame(width: tabItemWidth)
|
||||
if selectedTab == .home {
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .members {
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .records {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
}
|
||||
|
||||
var overlay: some View {
|
||||
HStack {
|
||||
if selectedTab == .mine { Spacer() }
|
||||
if selectedTab == .members {
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .records {
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
Rectangle()
|
||||
.fill(color)
|
||||
.frame(width: 28, height: 5)
|
||||
.cornerRadius(3)
|
||||
.frame(width: tabItemWidth)
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
|
||||
if selectedTab == .home {
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .members {
|
||||
Spacer()
|
||||
Spacer()
|
||||
}
|
||||
if selectedTab == .records {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
}
|
||||
}
|
||||
|
||||
struct TabBar_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
TabBar()
|
||||
.previewInterfaceOrientation(.landscapeLeft)
|
||||
TabBar()
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user