- 폰 첫 화면: 시작 버튼 위 워치 연결 배지(연결됨/미연결/미연동 — iOS 제약상 '연결됨'은 워치 앱 포그라운드 기준, 설정·도움말에 안내), 미연결 시작 시 확인 창 (기록은 동일함을 안내), 설정 > 애플워치 > '워치 미연결 경고' 토글. 단축어·autoStart는 경고 없이 통과 - 세팅 동기화 전면 제거(기기마다 진동 질감이 달라 각자 최적화) — 동기화는 기록만. 워치 시작 화면 왼쪽 스와이프로 전용 세팅 페이지(TabView .page): 준비/수축/이완/ 횟수 스테퍼(2줄 컴팩트 라벨), 진동 2종 피커(선택 즉시 미리 울림), 운동 유형·강도. 요약 라인 즉시 반영(SettingsStore EnvironmentObject) - 워치 진동 강화 재설계: 기본=.start 1방, 톡톡=.start 2방(0.35s), 지이이잉=.retry 2방 연결(~1.3s), 완주=.retry×3+.success(~2.5s), 세대 토큰 취소 - 도움말 4건·번역(en/ja) 갱신, CLAUDE.md 현행화 - 시뮬 검증: 배지·경고 창·설정 토글, 워치 세팅 페이지(46mm·40mm)·요약 반영, 3종 빌드 그린·경고 0·l10n 0/0/0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
90 lines
3.6 KiB
Swift
90 lines
3.6 KiB
Swift
//
|
||
// WatchSettingsView.swift
|
||
// KegingWatch Watch App
|
||
//
|
||
// 워치 전용 세팅 — 시작 화면에서 왼쪽으로 스와이프(7차). 세팅값은 아이폰과 독립이고
|
||
// (기기마다 진동 질감이 달라 각자 최적화), 기록만 동기화된다. 진동 패턴은 고르는
|
||
// 즉시 미리 울려서 손목에서 바로 느껴볼 수 있다.
|
||
//
|
||
|
||
import SwiftUI
|
||
|
||
struct WatchSettingsView: View {
|
||
@EnvironmentObject private var settings: SettingsStore
|
||
|
||
var body: some View {
|
||
NavigationStack {
|
||
Form {
|
||
Section {
|
||
Stepper(value: $settings.config.prepSeconds, in: 0...10) {
|
||
stepperLabel(Text("준비"), value: Text("\(settings.config.prepSeconds)초"))
|
||
}
|
||
Stepper(value: $settings.config.contractSeconds, in: 1...60) {
|
||
stepperLabel(Text("수축"), value: Text("\(settings.config.contractSeconds)초"))
|
||
}
|
||
Stepper(value: $settings.config.relaxSeconds, in: 1...60) {
|
||
stepperLabel(Text("이완"), value: Text("\(settings.config.relaxSeconds)초"))
|
||
}
|
||
Stepper(value: $settings.config.reps, in: 1...100) {
|
||
stepperLabel(Text("횟수"), value: Text("\(settings.config.reps)회"))
|
||
}
|
||
} header: {
|
||
Text("세트")
|
||
}
|
||
Section {
|
||
Picker(selection: $settings.config.contractPattern) {
|
||
ForEach(HapticPattern.allCases) { Text($0.label).tag($0) }
|
||
} label: {
|
||
Text("수축")
|
||
}
|
||
Picker(selection: $settings.config.relaxPattern) {
|
||
ForEach(HapticPattern.allCases) { Text($0.label).tag($0) }
|
||
} label: {
|
||
Text("이완")
|
||
}
|
||
} header: {
|
||
Text("진동")
|
||
} footer: {
|
||
Text("고르면 바로 진동이 울려요. 세팅은 아이폰과 따로 저장돼요.")
|
||
}
|
||
Section {
|
||
Picker(selection: $settings.config.healthWorkoutType) {
|
||
ForEach(HealthWorkoutType.allCases) { Text($0.label).tag($0) }
|
||
} label: {
|
||
Text("운동 유형")
|
||
}
|
||
Picker(selection: $settings.config.effortScore) {
|
||
Text("안 함").tag(0)
|
||
ForEach(1...10, id: \.self) { score in
|
||
Text(verbatim: "\(score)").tag(score)
|
||
}
|
||
} label: {
|
||
Text("운동 강도")
|
||
}
|
||
} header: {
|
||
Text("애플 건강")
|
||
}
|
||
}
|
||
.navigationTitle("세팅")
|
||
.onChange(of: settings.config.contractPattern) { _, newValue in
|
||
WatchHaptics.play(newValue)
|
||
}
|
||
.onChange(of: settings.config.relaxPattern) { _, newValue in
|
||
WatchHaptics.play(newValue)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 워치 스테퍼의 −·+ 사이에 들어가는 컴팩트 2줄 라벨 (이름 캡션 + 값)
|
||
private func stepperLabel(_ title: Text, value: Text) -> some View {
|
||
VStack(spacing: 0) {
|
||
title
|
||
.font(.caption2)
|
||
.foregroundStyle(.secondary)
|
||
value
|
||
.font(.body.weight(.semibold))
|
||
.monospacedDigit()
|
||
}
|
||
}
|
||
}
|