// // OnboardingView.swift // Haru_Danim // // 첫 실행 온보딩 — 행동 → 목표·다짐 → 정리, 3장 (CLAUDE.md §6). // 데이터가 하나도 없는 진짜 첫 사용자에게만 1회 자동 표시되고(건너뛰어도 재표시 없음), // 설정 → 지원의 '앱 소개 다시 보기'로 언제든 다시 볼 수 있다. // 시드·마케팅 촬영 플로우는 시드가 행동을 먼저 만들어 자동으로 건너뛰어진다. // import SwiftUI struct OnboardingView: View { /// 한 번 보면(건너뛰기 포함) 다시 자동 표시하지 않는 플래그 (standard — 기기 로컬) static let doneKey = "onboarding.done" var onFinish: () -> Void @State private var page = 0 private let lastPage = 2 var body: some View { ZStack(alignment: .topTrailing) { AppTheme.background.ignoresSafeArea() TabView(selection: $page) { pageView( symbol: "bolt.fill", color: AppTheme.green, title: "버튼 한 번으로 기록", text: "'행동'을 만들면 탭 한 번으로 시간 측정을 시작·종료하거나 횟수를 +1 기록할 수 있어요. 여러 행동을 동시에 측정해도 돼요." ) .tag(0) pageView( symbol: "flag.checkered", color: AppTheme.green, title: "목표와 다짐으로 꾸준하게", text: "큰 '목표' 아래에 실천 단위인 '다짐'을 만들어요. 하루·주간·월간 진행률과 연속 달성이 자동으로 계산되고, 목표가 끝나면 달성 여부도 판정돼요." ) .tag(1) pageView( symbol: "chart.xyaxis.line", color: AppTheme.yellow, title: "정리하고 돌아보기", text: "꼬리표로 행동을 색깔별로 분류하고, 기록·통계 탭에서 하루를 돌아봐요. 더 자세한 사용법은 설정 탭의 도움말에 있어요." ) .tag(2) } .tabViewStyle(.page(indexDisplayMode: .always)) .indexViewStyle(.page(backgroundDisplayMode: .always)) if page < lastPage { Button("건너뛰기") { finish() } .font(.subheadline.weight(.medium)) .foregroundStyle(.secondary) .padding(20) } } .safeAreaInset(edge: .bottom) { Button { if page < lastPage { withAnimation { page += 1 } } else { finish() } } label: { Text(page < lastPage ? String(localized: "다음") : String(localized: "시작하기")) .font(.headline) .frame(maxWidth: .infinity) .padding(.vertical, 6) } .buttonStyle(.borderedProminent) .buttonBorderShape(.capsule) .tint(AppTheme.green) .padding(.horizontal, 28) .padding(.bottom, 18) } } private func finish() { UserDefaults.standard.set(true, forKey: Self.doneKey) onFinish() } private func pageView(symbol: String, color: Color, title: LocalizedStringKey, text: LocalizedStringKey) -> some View { VStack(spacing: 22) { Spacer() Image(systemName: symbol) .font(.system(size: 64, weight: .semibold)) .foregroundStyle(color) .frame(width: 130, height: 130) .background(color.opacity(0.12), in: RoundedRectangle(cornerRadius: 34, style: .continuous)) Text(title) .font(.title2.bold()) .multilineTextAlignment(.center) Text(text) .font(.callout) .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 36) .frame(maxWidth: 520) Spacer() Spacer() } .frame(maxWidth: .infinity) } }