feat(app): add Home tab refresh button and device-specific help guide
- 모음 탭 왼쪽 위 새로고침 버튼(RefreshButton/AppRefresh): 루트 뷰 identity 리셋으로 모든 @Query를 재조회(앱 재시작과 같은 효과) + 라이브 액티비티· 위젯·워치 스냅숏 동기화. 다른 기기의 iCloud 변경이 화면에 늦게 보일 때 재시작 없이 반영. iPhone·iPad 공통, Release(스토어) 빌드에도 포함 - 설정 → 지원 → 도움말(HelpView): 기본 개념/모음 탭/목표/기록·통계/동기화/ 프리미엄 공통 + iPhone은 화면 구성(탭바·버블 메뉴·다이나믹 아일랜드)과 애플워치(기록·컴플리케이션·동기화) 섹션, iPad는 일기 섹션. 접이식 주제별 구성, 새 문자열 59개 en/ja 번역 - 검증 인자: -helpPreview YES Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yKDMhuF39GVYy3FMcGHh7
This commit is contained in:
parent
040198beb4
commit
957618fccd
@ -18,10 +18,13 @@ struct ContentView: View {
|
||||
#if DEBUG
|
||||
// 검증용: -widgetPreview YES(홈 위젯) / lock(잠금화면 위젯) → 위젯 미리보기 화면
|
||||
// -premiumPreview YES → 프리미엄 결제 화면 직접 표시
|
||||
// -helpPreview YES → 도움말 화면 직접 표시
|
||||
if let mode = UserDefaults.standard.string(forKey: "widgetPreview") {
|
||||
WidgetPreviewScreen(showLock: mode == "lock")
|
||||
} else if UserDefaults.standard.bool(forKey: "premiumPreview") {
|
||||
NavigationStack { PremiumView() }
|
||||
} else if UserDefaults.standard.bool(forKey: "helpPreview") {
|
||||
NavigationStack { HelpView() }
|
||||
} else {
|
||||
RootNavigationView()
|
||||
}
|
||||
@ -101,6 +104,7 @@ struct SplashView: View {
|
||||
/// iPad·Mac은 항상 사이드바를 사용한다 (반원 메뉴는 iPhone 전용).
|
||||
struct RootNavigationView: View {
|
||||
@AppStorage(SettingsKeys.navStyle) private var navStyleRaw = NavStyle.tabBar.rawValue
|
||||
private let refresh = AppRefresh.shared
|
||||
|
||||
/// DEBUG 검증용: `-navStyle radial` 런치 인자로 설정과 무관하게 강제 전환
|
||||
private var effectiveNavStyle: NavStyle {
|
||||
@ -114,13 +118,17 @@ struct RootNavigationView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if DeviceLayout.isPad {
|
||||
SidebarRootView()
|
||||
} else if effectiveNavStyle == .radial {
|
||||
RadialTabView()
|
||||
} else {
|
||||
MainTabView()
|
||||
Group {
|
||||
if DeviceLayout.isPad {
|
||||
SidebarRootView()
|
||||
} else if effectiveNavStyle == .radial {
|
||||
RadialTabView()
|
||||
} else {
|
||||
MainTabView()
|
||||
}
|
||||
}
|
||||
// 새로고침 토큰이 바뀌면 화면 전체를 다시 만들어 모든 @Query가 재조회된다
|
||||
.id(refresh.token)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
64
myApp/HaruDanim/IOS/Core/AppRefresh.swift
Normal file
64
myApp/HaruDanim/IOS/Core/AppRefresh.swift
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// AppRefresh.swift
|
||||
// Haru_Danim
|
||||
//
|
||||
// 수동 새로고침 (모음 탭 버튼)
|
||||
// - iCloud 동기화로 다른 기기에서 바뀐 데이터가 화면에 바로 안 보일 때, 앱을 껐다 켜는
|
||||
// 대신 쓰는 버튼. token을 바꾸면 루트 뷰 identity가 리셋되어 모든 @Query가 스토어에서
|
||||
// 다시 조회된다 (재시작과 같은 효과, 데이터는 그대로).
|
||||
// - 원격 변경 자동 반영(CloudSyncMonitor)은 위젯·워치·라이브 액티비티만 갱신한다.
|
||||
// 화면 전체 리셋은 입력 중인 시트를 닫아버릴 수 있어 수동 버튼으로만 한다.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Observation
|
||||
import SwiftData
|
||||
import WidgetKit
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class AppRefresh {
|
||||
static let shared = AppRefresh()
|
||||
|
||||
/// RootNavigationView가 .id로 사용 — 바뀌면 화면 전체가 다시 만들어진다
|
||||
private(set) var token = UUID()
|
||||
private(set) var isRefreshing = false
|
||||
|
||||
func refreshNow() {
|
||||
guard !isRefreshing else { return }
|
||||
isRefreshing = true
|
||||
token = UUID()
|
||||
Task { @MainActor in
|
||||
// 화면 재구성과 함께 파생 상태도 최신 데이터로 맞춘다
|
||||
LiveActivityManager.sync(context: DataStore.shared.mainContext)
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
WatchSyncManager.shared.pushSnapshot()
|
||||
// 버튼이 눌렸다는 피드백이 보일 만큼만 잠깐 유지
|
||||
try? await Task.sleep(for: .milliseconds(700))
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 새로고침 버튼 (모음 탭 툴바)
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// 다른 기기에서 바뀐 데이터를 다시 불러오는 작은 툴바 버튼
|
||||
struct RefreshButton: View {
|
||||
private let refresh = AppRefresh.shared
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
refresh.refreshNow()
|
||||
} label: {
|
||||
if refresh.isRefreshing {
|
||||
ProgressView()
|
||||
} else {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
}
|
||||
}
|
||||
.disabled(refresh.isRefreshing)
|
||||
.accessibilityLabel(Text("새로고침"))
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
264
myApp/HaruDanim/IOS/Views/HelpView.swift
Normal file
264
myApp/HaruDanim/IOS/Views/HelpView.swift
Normal file
@ -0,0 +1,264 @@
|
||||
//
|
||||
// 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: "checkmark.seal.fill",
|
||||
title: "목표 종료와 판정",
|
||||
body: "종료일이 있는 목표는 날짜가 지나면 자동으로 달성 여부가 판정되고, 종료일이 없는 목표는 수동 종료 버튼으로 마칠 수 있어요. 다짐 없이 종료일이 지난 목표는 '달성했나요?' 상태가 되어 직접 선택해요. 완료된 목표는 목록 하단의 완료 보관함에서 볼 수 있어요."
|
||||
),
|
||||
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: "기록·통계 탭 오른쪽 위 내보내기 버튼으로 지금 보고 있는 기간의 리포트를 고해상도 이미지로 만들 수 있어요. 라이트·다크 테마를 골라 공유하거나 사진에 저장해요."
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
// 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: "applepencil",
|
||||
title: "펜슬 노트 페이지",
|
||||
body: "페이지를 넘기면 자유 노트가 이어져요. 애플 펜슬로 쓰고 그리고, '사진'과 '도형'을 추가한 뒤 '배치 모드'에서 드래그·핀치로 옮기고 크기를 바꿔요. 두 손가락 핀치로 페이지를 확대해 세밀하게 쓸 수 있고, 줄 노트를 켜면 줄 간격도 고를 수 있어요."
|
||||
),
|
||||
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: "bolt.horizontal.circle",
|
||||
title: "다이나믹 아일랜드·잠금화면",
|
||||
body: "시간을 측정 중이면 다이나믹 아일랜드와 잠금화면에 진행 시간이 표시돼요. 여러 개를 동시에 측정하면 설정의 '대표 시간 기준'(먼저/나중에 시작한 행동)에 따라 하나가 대표로 보이고 나머지는 +N으로 표시돼요."
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
// 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: "arrow.counterclockwise.circle",
|
||||
title: "구매 복원과 구독 관리",
|
||||
body: "기기를 바꿨거나 앱을 다시 설치했다면 설정 → 프리미엄의 '구매 복원'을 눌러 주세요. 구독 해지나 플랜 변경은 '구독 관리'에서 언제든 할 수 있고, 해지해도 결제한 기간이 끝날 때까지는 계속 쓸 수 있어요."
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
@ -76,6 +76,10 @@ struct MainView: View {
|
||||
.background(AppTheme.background)
|
||||
.navigationTitle("모음")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
// 다른 기기에서 바뀐 데이터를 앱 재시작 없이 다시 불러오는 새로고침
|
||||
RefreshButton()
|
||||
}
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button(isEditing ? String(localized: "완료") : String(localized: "배치 편집")) {
|
||||
withAnimation {
|
||||
|
||||
@ -166,7 +166,12 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
.id("premiumSection")
|
||||
Section {
|
||||
Section("지원") {
|
||||
NavigationLink {
|
||||
HelpView()
|
||||
} label: {
|
||||
Label("도움말", systemImage: "questionmark.circle")
|
||||
}
|
||||
HStack {
|
||||
Text("버전")
|
||||
Spacer()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user