mycode/myApp/Keging/IOS/HelpView.swift
songyc macbook eba01b570f fix(keging): 실기기 2차 피드백 — 중단 UI 인라인화·통계 위로 스와이프·진동 3종 Core Haptics 재설계
① 중단 확인: 하단 시트(confirmationDialog) → 버튼 자리에서 펼쳐지는 인라인 확인
   (안내 문구 + 계속하기(그린)/중단하기(레드), scale+opacity 전환)
② 통계: 아래→위 스와이프로 반전, 하단 중앙 손잡이(위 화살표+캡슐), 통통 튀는 스프링
   (dampingFraction 0.62), 통계 상단에 닫기 손잡이 추가, 닫기=아래로 스와이프
③ 진동 3종(기본·톡톡·지이이잉)으로 재설계 — 체이닝 간격도 길다는 피드백:
   - iOS 포그라운드: Core Haptics 정밀 재생(Shared/HapticEnginePlayer) — 기본 0.25s,
     톡톡 0.14s×2 시작 간격 0.28s(요구 0.5s 미만), 지이이잉 1.2s 연속, 세기 1.0
   - iOS 백그라운드 폴백: 시스템 바이브 완료 체이닝(1방/2방/3방 즉시 잇기)
   - 워치: watchOS SDK에 CoreHaptics 부재(실측) — 강한 시스템 햅틱 매핑
     (기본=.notification, 톡톡=.directionUp, 지이이잉=.retry — 내장이라 간격 뭉개짐 없음)
   - 구버전 저장 패턴 자동 이관, 기본값 수축=지이이잉·이완=기본
- 신규 QA 인자 -confirmStop, 도움말 문구·번역(en/ja) 갱신, stale 정리(카탈로그 0/0)
- 검증: Debug·Release·워치 빌드 그린, 화면 QA(메인 하단 손잡이·통계 시트·중단 인라인 레드)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
2026-08-30 09:39:37 +09:00

139 lines
8.9 KiB
Swift
Raw Permalink 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.

//
// HelpView.swift
// Keging
//
// . .
//
import SwiftUI
struct HelpView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
List {
Section {
helpRow(
symbol: "play.circle.fill",
title: Text("바로 시작하기"),
body: Text("첫 화면의 큰 시작 버튼을 누르면 저장된 세팅대로 운동이 시작돼요. 준비 시간을 설정해 두면 그만큼 카운트다운 후에 첫 수축이 시작돼요. 화면의 원이 수축할 때 함께 조이고, 커질 때 힘을 풀면 돼요. 정해진 횟수를 모두 마치면 완료 화면이 뜨고 기록이 저장돼요.")
)
helpRow(
symbol: "slider.horizontal.3",
title: Text("운동 세팅 바꾸기"),
body: Text("오른쪽 위 슬라이더 버튼이 운동 세팅이에요. 준비 시간(010초), 수축 시간(160초), 이완 시간(160초), 반복 횟수(1100회)를 정할 수 있어요. 예를 들어 수축 10초·이완 5초·20회로 설정하면, 수축과 이완을 20번 반복하는 한 세트가 약 5분이 돼요. 시작 버튼 아래의 카드를 눌러도 이 화면이 열려요.")
)
helpRow(
symbol: "iphone.radiowaves.left.and.right",
title: Text("진동으로 구분하기"),
body: Text("수축이 시작될 때와 이완이 시작될 때 서로 다른 진동이 울려요. 그래서 화면을 보지 않아도 지금 조여야 할지 풀어야 할지 알 수 있어요. 기본값은 수축=지이이잉(아주 긴 진동), 이완=기본(짧은 한 번)이라 첫 순간의 질감으로 바로 구분돼요. 패턴은 운동 세팅에서 국면별로 고를 수 있고(기본·톡톡·지이이잉), '미리 느끼기' 버튼으로 실제 진동을 확인할 수 있어요. 워치에서도 같은 패턴으로 울려요. 진동은 실기기에서만 느껴져요.")
)
helpRow(
symbol: "stop.circle",
title: Text("중간에 중단하면"),
body: Text("운동 중 아래의 중단 버튼을 누르면 확인 후 세트가 끝나요. 중단한 세트는 아예 하지 않은 것으로 처리돼서 기록과 통계에 남지 않아요. 끝까지 마친 세트만 기록돼요.")
)
} header: {
Text("기본 사용법")
}
Section {
helpRow(
symbol: "chart.xyaxis.line",
title: Text("통계 열기"),
body: Text("첫 화면을 위로 쓸어올리거나 아래 가운데의 손잡이를 누르면 통계 화면이 올라와요. 닫을 때는 아래로 쓸어내리거나 X 버튼을 누르면 돼요.")
)
helpRow(
symbol: "calendar",
title: Text("하루 · 주간 · 월간"),
body: Text("하루 탭에서는 오늘 몇 시에 몇 회를 했는지 목록과 시간대별 꺾은선 그래프로 보여줘요. 주간·월간 탭에서는 일별 횟수를 꺾은선 그래프와 합계(세트·횟수)로 보여줘요. 주는 월요일에 시작해요.")
)
helpRow(
symbol: "arrow.left.arrow.right",
title: Text("이번 주 ↔ 지난 7일 전환"),
body: Text("주간·월간 화면 위의 스위치로 두 가지 방식을 오갈 수 있어요. '이번 주 / 이번 달'은 달력 기준(월요일~일요일, 1일말일)이고, '지난 7일 / 지난 30일'은 오늘을 기준으로 거슬러 세는 방식이에요.")
)
} header: {
Text("통계")
}
Section {
helpRow(
symbol: "bolt.horizontal.circle",
title: Text("다이나믹 아일랜드"),
body: Text("운동 중에 앱을 나가면 다이나믹 아일랜드에 현재 몇 번째인지와 수축·이완 상태가 떠요. 길게 누르면 남은 시간과 진행 바가 보이는 큰 화면이 열려요. 진동도 계속 울리니까 다른 앱을 쓰면서도 운동을 이어갈 수 있어요.")
)
helpRow(
symbol: "square.and.arrow.up",
title: Text("단축어와 시리"),
body: Text("단축어 앱에서 '케겔 운동 시작' 동작을 추가할 수 있어요. 실행하면 케깅이 열리면서 저장된 세팅대로 바로 시작돼요. 홈 화면 아이콘·잠금화면 위젯·뒷면 탭 등 단축어를 부를 수 있는 곳이라면 어디서든 쓸 수 있어요.")
)
} header: {
Text("앱 밖에서")
}
Section {
helpRow(
symbol: "applewatch",
title: Text("애플워치에서 운동하기"),
body: Text("워치 앱을 열면 시작 버튼 하나만 있어요. 누르면 바로 시작되고, 화면에 수축·이완 상태와 남은 시간, 몇 번째인지가 한눈에 보여요. 워치에서 실행하면 진동은 워치에서만 울려요. 중단 버튼을 누르면 그 세트는 기록되지 않아요.")
)
helpRow(
symbol: "arrow.triangle.2.circlepath",
title: Text("워치와 아이폰 동기화"),
body: Text("운동 세팅은 아이폰에서만 바꿀 수 있고, 바꾸면 워치에 자동으로 전달돼요. 워치에서 끝까지 마친 세트는 아이폰으로 넘어와 통계에 합쳐져요. 워치가 아이폰과 떨어져 있어도 기록은 쌓였다가 연결되면 자동으로 전송돼요.")
)
} header: {
Text("애플워치")
}
Section {
helpRow(
symbol: "heart.circle",
title: Text("애플 건강 연동"),
body: Text("운동을 끝까지 마치면 그 시간이 애플 건강에 운동으로 자동 기록돼요. 운동 유형(기타·코어 트레이닝)과 운동 강도(110)는 설정에서 고를 수 있어요. 처음 시작할 때 건강 권한을 물어보고, 허용하면 그다음부터는 알아서 저장돼요. 워치에서 하면 워치가 심박수와 함께 기록해요. 중단한 세트는 건강에도 기록되지 않고, 원치 않으면 건강 앱에서 케깅의 권한을 끄면 돼요.")
)
helpRow(
symbol: "paintbrush",
title: Text("테마와 언어"),
body: Text("왼쪽 위 톱니바퀴 버튼이 설정이에요. 화면 모드(시스템·라이트·다크)와 언어(시스템·한국어·English·日本語)를 고를 수 있어요. 언어는 앱을 완전히 종료했다가 다시 실행하면 적용돼요. 워치는 워치의 시스템 언어를 따라요.")
)
helpRow(
symbol: "lock.shield",
title: Text("데이터와 개인정보"),
body: Text("케깅은 광고와 결제가 없는 완전 무료 앱이에요. 모든 기록은 이 기기(그리고 워치)에만 저장되고, 외부 서버로 전송되지 않아요.")
)
} header: {
Text("설정·기타")
}
}
.navigationTitle("도움말")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("완료") { dismiss() }
}
}
}
}
private func helpRow(symbol: String, title: Text, body: Text) -> some View {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 8) {
Image(systemName: symbol)
.font(.body)
.foregroundStyle(AppTheme.green)
.frame(width: 24)
title
.font(.headline)
}
body
.font(.subheadline)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.vertical, 4)
}
}