mycode/myApp/Haru_Danim/IOS/ContentView.swift

177 lines
5.4 KiB
Swift

//
// ContentView.swift
// Haru_Danim
//
// + 6 (CLAUDE.md §5)
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var context
@AppStorage(SettingsKeys.theme) private var theme: String = "light"
@State private var showSplash = true
var body: some View {
ZStack {
MainTabView()
if showSplash {
SplashView()
.transition(.opacity)
.zIndex(1)
}
}
.tint(AppTheme.green)
.preferredColorScheme(theme == "dark" ? .dark : .light)
.task {
#if DEBUG
DebugSeed.seedIfRequested(context: context)
DebugSeed.autoStartIfRequested(context: context)
#endif
try? await Task.sleep(for: .seconds(1.2))
withAnimation(.easeOut(duration: 0.4)) {
showSplash = false
}
}
}
}
// MARK: - ( + , CLAUDE.md §5.1)
struct SplashView: View {
@State private var appeared = false
var body: some View {
ZStack {
// ( ) /
AppTheme.background.ignoresSafeArea()
VStack(spacing: 18) {
Image("AppLogo")
.resizable()
.scaledToFit()
.frame(width: 150, height: 150)
.clipShape(RoundedRectangle(cornerRadius: 34, style: .continuous))
.shadow(color: .black.opacity(0.12), radius: 14, y: 6)
.scaleEffect(appeared ? 1 : 0.85)
.opacity(appeared ? 1 : 0)
Text("하루 다님")
.font(.largeTitle.bold())
.foregroundStyle(AppTheme.green)
.opacity(appeared ? 1 : 0)
Text("하루 습관 및 시간 추적")
.font(.subheadline)
.foregroundStyle(.secondary)
.opacity(appeared ? 1 : 0)
}
}
.onAppear {
withAnimation(.spring(duration: 0.5)) {
appeared = true
}
}
}
}
// MARK: - ( 6, 5 )
enum AppTab: String, CaseIterable, Identifiable {
case main, action, tag, goal, history, settings
var id: String { rawValue }
var label: String {
switch self {
case .main: return "메인"
case .action: return "행동"
case .tag: return "꼬리표"
case .goal: return "목표"
case .history: return "기록"
case .settings: return "설정"
}
}
var symbol: String {
switch self {
case .main: return "square.grid.2x2.fill"
case .action: return "figure.walk"
case .tag: return "tag.fill"
case .goal: return "flag.checkered"
case .history: return "calendar"
case .settings: return "gearshape.fill"
}
}
}
struct MainTabView: View {
@State private var selection: AppTab = {
#if DEBUG
if let raw = UserDefaults.standard.string(forKey: "startTab"),
let tab = AppTab(rawValue: raw) {
return tab
}
#endif
return .main
}()
var body: some View {
ZStack {
switch selection {
case .main: MainView()
case .action: ActionListView()
case .tag: TagListView()
case .goal: GoalListView()
case .history: HistoryView()
case .settings: SettingsView()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.safeAreaInset(edge: .bottom) {
tabBar
}
.background(AppTheme.background)
}
///
private var tabBar: some View {
HStack(spacing: 0) {
ForEach(AppTab.allCases) { tab in
Button {
withAnimation(.spring(duration: 0.25)) {
selection = tab
}
} label: {
VStack(spacing: 3) {
Image(systemName: tab.symbol)
.font(.system(size: 19, weight: .medium))
Text(tab.label)
.font(.system(size: 10, weight: .semibold))
}
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background {
if selection == tab {
RoundedRectangle(cornerRadius: 14, style: .continuous)
.fill(AppTheme.green.opacity(0.16))
}
}
.foregroundStyle(selection == tab ? AppTheme.green : Color.secondary)
}
.buttonStyle(.plain)
}
}
.padding(6)
.glassEffect(.regular, in: RoundedRectangle(cornerRadius: 26, style: .continuous))
.padding(.horizontal, 10)
.padding(.bottom, 4)
}
}
#Preview {
ContentView()
.modelContainer(for: [
Tag.self, Action.self, TimeSession.self,
CountEntry.self, Goal.self, Quest.self,
], inMemory: true)
}