mycode/myApp/HaruDanim/IOS/Views/HelpView.swift
songyc macbook bdb97e3c9f feat(diary): imported page templates — PDF/photo backgrounds with per-page picker
일기 노트 페이지 양식(속지) 기능 (합의된 설계 그대로):

- 모델: DiaryTemplate 신설(원본 externalStorage — 여러 쪽 PDF도
  통째 1개만 저장 + pageCount) + DiaryPage.templateID/
  templatePageIndex(기본값 있는 새 필드, uuid 문자열 참조 —
  CloudKit 규칙·동기화 순서에 안전). 스키마에 등록.
- 양식 관리: 달력 툴바에서 진입 — PDF(파일, ≤20MB)·사진 가져오기,
  이름 변경·삭제(사용 중 페이지 수 경고 → 빈 캔버스 폴백, 필기 보존),
  첫 쪽 썸네일 저장. 쪽 상한 24쪽(초과분 안내).
- 페이지 추가: 양식이 있으면 [빈 캔버스/줄 노트/내 양식] 선택 시트,
  여러 쪽 PDF는 2단계 쪽 썸네일 선택(지연 생성). 양식이 없으면
  기존처럼 즉시 추가 — 흔한 경로의 반응성 유지.
- 렌더: 페이지 논리 좌표(768×1024)에 aspect-fit — 필기 좌표계 불변.
  PDF는 벡터 원본을 보존하고 줌 종료 시 현재 배율로 백그라운드
  재래스터(펜슬 캔버스 선명도 훅에 함께 연결 — 확대해도 흐림 없음).
  이미지는 가져올 때 긴 변 3072px 리샘플(필기 래스터 상한 3×와 동일).
  양식 페이지는 줄 노트 토글 숨김(상호 배타), 양식만 깔린 페이지도
  작성함 취급(hasContent). PencilKit 캔버스 코드는 건드리지 않음.
- 내보내기(PDF/PNG)에도 양식 배경 동일 반영.

검증(iPad 시뮬레이터, -diarySeedTemplate 등 신규 DEBUG 인자):
양식 관리·페이지 선택 시트·양식 페이지 렌더(2× 프로그램 줌에서
점 노트 선명) 스크린샷 + 이미지 내보내기 산출물에 양식 포함 확인.
Debug·Store 빌드 성공, 신규 문자열 22종 ko/en/ja(missing 0).
도움말 '나만의 양식(속지)' 항목 추가, CLAUDE.md §3·§6.7·§14 갱신.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
2026-07-14 19:10:50 +09:00

300 lines
20 KiB
Swift

//
// HelpView.swift
// Haru_Danim
//
// : .
// iPhone / · ·,
// iPad ·(). DisclosureGroup.
//
import SwiftUI
// MARK: -
private struct HelpTopic: Identifiable {
let id = UUID()
let symbol: String
let title: LocalizedStringKey
let body: LocalizedStringKey
}
private struct HelpGroup: Identifiable {
let id = UUID()
let title: LocalizedStringKey
let topics: [HelpTopic]
}
// MARK: -
struct HelpView: View {
private var groups: [HelpGroup] {
var result: [HelpGroup] = [basics, mainTab, goals, history, sync]
if DeviceLayout.isPad {
result.append(diary)
} else {
result.append(iphoneUI)
result.append(watch)
}
result.append(premiumHelp)
return result
}
var body: some View {
List {
ForEach(groups) { group in
Section(group.title) {
ForEach(group.topics) { topic in
DisclosureGroup {
Text(topic.body)
.font(.subheadline)
.foregroundStyle(.secondary)
.padding(.vertical, 4)
} label: {
Label {
Text(topic.title)
.font(.body.weight(.medium))
} icon: {
Image(systemName: topic.symbol)
.foregroundStyle(AppTheme.green)
}
}
}
}
}
}
.scrollContentBackground(.hidden)
.background(AppTheme.background)
.navigationTitle("도움말")
.navigationBarTitleDisplayMode(.inline)
.toolbar(.hidden, for: .tabBar)
}
// MARK:
private var basics: HelpGroup {
HelpGroup(title: "기본 개념", topics: [
HelpTopic(
symbol: "figure.walk",
title: "행동 — 추적의 최소 단위",
body: "행동 탭에서 습관이나 할 일을 '행동'으로 등록해요. 만들 때 시간 추적형(시작·종료 토글로 소요 시간 측정) 또는 횟수 추적형(탭할 때마다 +1) 중 하나를 골라요. 이름·아이콘·꼬리표는 나중에 언제든 바꿀 수 있지만 추적 방식은 바꿀 수 없으니 처음에 잘 골라 주세요."
),
HelpTopic(
symbol: "tag.fill",
title: "꼬리표 — 색으로 분류",
body: "꼬리표는 행동을 묶는 태그예요. 고유의 색을 가지며, 행동 버튼의 색은 지정한 꼬리표의 색을 따라요. 하나의 행동에 여러 꼬리표를 붙일 수 있고, 꼬리표 탭이나 행동 만들기 화면에서 바로 추가할 수 있어요."
),
HelpTopic(
symbol: "flag.fill",
title: "목표와 다짐",
body: "목표는 '토익 700점 받기'처럼 큰 방향이고, 다짐은 그 아래 실천 단위예요. 다짐은 특정 행동(또는 꼬리표)을 골라 주기(하루·주간·월간·특정 기간)와 목표량을 정해요. '이상 달성'뿐 아니라 '이하 유지'(예: 영상 시청 하루 1시간 이하)도 만들 수 있어요."
),
])
}
// MARK:
private var mainTab: HelpGroup {
HelpGroup(title: "모음 탭", topics: [
HelpTopic(
symbol: "play.circle.fill",
title: "측정 시작·종료",
body: "행동 버튼을 탭하면 시간형은 측정이 시작되고 다시 탭하면 종료돼요(테두리로 측정 중을 구분). 횟수형은 탭할 때마다 1씩 올라가요. 여러 행동을 동시에 측정해도 되고, 측정 중인 행동은 상단 '현재 진행 중' 영역에서도 종료할 수 있어요. 끄는 걸 자주 잊는다면 설정 → 측정의 '장시간 측정 알림'을 켜 보세요 — 정한 시간을 넘기면 알려줘요."
),
HelpTopic(
symbol: "hand.tap.fill",
title: "길게 누르기 메뉴",
body: "행동 버튼을 길게 누르면 기록 직접 입력·수정, 메모, 기록 확인, 통계 보기, 행동 설정 수정, 삭제를 할 수 있어요. 시간형은 시작·종료 시각을, 횟수형은 횟수와 시각을 직접 고칠 수 있어요."
),
HelpTopic(
symbol: "square.grid.3x3.fill",
title: "배치 편집",
body: "오른쪽 위 '배치 편집'을 누르면 버튼이 흔들리며 드래그로 순서를 바꿀 수 있어요. 한 줄에 몇 개를 놓을지도 고를 수 있어요. '완료'를 누르면 저장되고, 이 순서는 위젯의 기본 표시 순서에도 쓰여요."
),
HelpTopic(
symbol: "arrow.clockwise",
title: "새로고침 버튼",
body: "왼쪽 위 새로고침 버튼은 화면을 앱을 껐다 켠 것처럼 다시 불러와요. 다른 기기(아이패드·아이폰·워치)에서 기록했는데 이 기기 화면에 아직 안 보일 때 눌러 주세요. iCloud 동기화가 도착해 있다면 바로 반영돼요."
),
])
}
// MARK:
private var goals: HelpGroup {
HelpGroup(title: "목표 탭", topics: [
HelpTopic(
symbol: "chart.bar.fill",
title: "진행률 읽는 법",
body: "다짐마다 하루·주간·월간 진행률이 표시돼요. 주간·월간 진행률은 하루 초과 달성이 다른 날의 미달을 가리지 않도록 하루 목표량까지만 인정해 합산해요. '이하 유지' 다짐은 한도를 넘지 않는 동안 100%로 표시돼요."
),
HelpTopic(
symbol: "flame.fill",
title: "연속 달성 표시",
body: "다짐을 끊기지 않고 지키면 불꽃과 함께 '연속 3일'처럼 표시돼요. 요일이나 날짜를 정한 다짐은 그 수행일만 세고, 주간·월간 다짐은 '연속 N주·N달'로 세요. 계산은 목표 시작일부터 시작하고, 아직 끝나지 않은 오늘(이번 주기)은 못 채웠어도 끊기지 않아요. '이하 유지' 다짐은 한도를 넘는 순간 끊겨요."
),
HelpTopic(
symbol: "checkmark.seal.fill",
title: "목표 종료와 판정",
body: "종료일이 있는 목표는 날짜가 지나면 자동으로 달성 여부가 판정되고, 종료일이 없는 목표는 수동 종료 버튼으로 마칠 수 있어요. 판정은 다짐들의 평균 달성률이 목표를 만들 때 정한 '달성 판정 기준'(기본 100%) 이상이면 달성이에요 — 예를 들어 기준을 80%로 두면 조금 못 채워도 달성으로 기록돼요. 다짐 없이 종료일이 지난 목표는 '달성했나요?' 상태가 되어 직접 선택해요. 완료된 목표는 목록 하단의 완료 보관함에서 볼 수 있어요."
),
HelpTopic(
symbol: "pin.fill",
title: "모음 탭에 목표 카드 띄우기",
body: "설정 → '모음 탭 목표 진행 현황'에서 원하는 목표를 선택하면 모음 탭 상단에 진행 현황 카드가 떠요. 여러 개를 선택하면 옆으로 쓸어 넘길 수 있어요."
),
])
}
// MARK: ·
private var history: HelpGroup {
HelpGroup(title: "기록·통계 탭", topics: [
HelpTopic(
symbol: "calendar",
title: "기록 보기·고치기",
body: "기록 탭은 하루의 모든 기록을 보여줘요. 상단 화살표로 날짜를 이동하고, 기록을 탭하면 바로 수정할 수 있어요. 타임테이블 보기로 전환하면 하루 또는 일주일을 24시간 축 위에서 한눈에 볼 수 있어요."
),
HelpTopic(
symbol: "line.3.horizontal.decrease.circle",
title: "필터와 하루 기준",
body: "필터 버튼으로 보고 싶은 행동만 골라 볼 수 있어요. 설정의 '주 시작 요일'과 '하루 시작 시간'이 모든 집계에 적용돼요. 예를 들어 하루 시작을 06:00으로 두면 새벽 기록은 전날 실적으로 계산돼요."
),
HelpTopic(
symbol: "square.and.arrow.up",
title: "이미지로 내보내기",
body: "기록·통계 탭 오른쪽 위 내보내기 버튼으로 지금 보고 있는 기간의 리포트를 고해상도 이미지로 만들 수 있어요. 라이트·다크 테마를 골라 공유하거나 사진에 저장해요."
),
HelpTopic(
symbol: "tablecells",
title: "CSV로 백업하기",
body: " ' ' (····) CSV . . ."
),
])
}
// MARK: 공통 — 동기화
private var sync: HelpGroup {
HelpGroup(title: " ()", topics: [
HelpTopic(
symbol: "icloud.fill",
title: " ",
body: " ' ' iCloud로 · . . Apple ."
),
HelpTopic(
symbol: "arrow.triangle.2.circlepath",
title: " ",
body: "iCloud . . ."
),
])
}
// MARK: iPad 전용 — 일기
private var diary: HelpGroup {
HelpGroup(title: " ( · )", topics: [
HelpTopic(
symbol: "book.closed.fill",
title: " ",
body: " . ·· · ·24 · . ' ' ."
),
HelpTopic(
symbol: "calendar.badge.plus",
title: " ·",
body: " ( ). , ' ' . , ."
),
HelpTopic(
symbol: "applepencil",
title: " ",
body: " . , ''·''·''( ) ' ' · . , . ' ' ."
),
HelpTopic(
symbol: "doc.text.image",
title: " ()",
body: " PDF나 . · , PDF는 . . ."
),
HelpTopic(
symbol: "square.and.arrow.up.on.square",
title: " ",
body: " ( ) PDF . ."
),
])
}
// MARK: iPhone 전용 — 화면 구성
private var iphoneUI: HelpGroup {
HelpGroup(title: " ()", topics: [
HelpTopic(
symbol: "squares.leading.rectangle",
title: " ",
body: " ' ' ( 3) . '' ."
),
HelpTopic(
symbol: "circle.hexagongrid.fill",
title: " ",
body: " ' ' ' ' . , . ' ' ."
),
HelpTopic(
symbol: "square.grid.2x2.fill",
title: " · ()",
body: " ' ' . ( ·+1), ( ), , ( ·), , . ' ' ·· , ."
),
HelpTopic(
symbol: "bolt.horizontal.circle",
title: " ·",
body: " . ' '(/ ) +N으로 ."
),
HelpTopic(
symbol: "switch.2",
title: " ()",
body: " ' ' ' ' , . / , +1. ."
),
])
}
// MARK: iPhone 전용 — 애플워치
private var watch: HelpGroup {
HelpGroup(title: " ()", topics: [
HelpTopic(
symbol: "applewatch",
title: " ",
body: " . , ·, +1 . '' . ."
),
HelpTopic(
symbol: "watchface.applewatch.case",
title: " ",
body: " '' . , , . · ."
),
HelpTopic(
symbol: "arrow.triangle.2.circlepath",
title: " ",
body: " . , . ."
),
])
}
// MARK: 공통 — 프리미엄
private var premiumHelp: HelpGroup {
HelpGroup(title: "", topics: [
HelpTopic(
symbol: "crown.fill",
title: " ",
body: " 10· 2· 3 . , , , . · . , ."
),
HelpTopic(
symbol: "mic.fill",
title: "· ",
body: " ' ' ·, , , · . \"시리야, 하루 다님에서 측정 시작\"처럼 말로도 실행할 수 있고, 어떤 행동인지 물으면 이름으로 대답하면 돼요. 진행률 동작은 값을 돌려주므로 다른 단축어와 이어 붙일 수 있어요."
),
HelpTopic(
symbol: "arrow.counterclockwise.circle",
title: "구매 복원과 구독 관리",
body: "기기를 바꿨거나 앱을 다시 설치했다면 설정 → 프리미엄의 '구매 복원'을 눌러 주세요. 구독 해지나 플랜 변경은 '구독 관리'에서 언제든 할 수 있고, 해지해도 결제한 기간이 끝날 때까지는 계속 쓸 수 있어요."
),
])
}
}