diff --git a/myApp/HaruDanim/IOS/ContentView.swift b/myApp/HaruDanim/IOS/ContentView.swift index 95f98c0..0d5625f 100644 --- a/myApp/HaruDanim/IOS/ContentView.swift +++ b/myApp/HaruDanim/IOS/ContentView.swift @@ -222,6 +222,22 @@ enum AppTab: String, CaseIterable, Identifiable { return tabs.isEmpty ? [.main] : Array(tabs.prefix(TabBarConfig.maxVisible)) } + /// 저장된 반원 메뉴 순서를 항상 모든 탭을 포함한 배열로 변환. + /// 방어 로직: 저장 문자열에 없거나(예: 나중에 추가된 탭) 잘못된 값은 기본(스펙) 순서로 맨 끝에 붙는다. + static func radialOrder(from raw: String) -> [AppTab] { + var result: [AppTab] = [] + var seen: Set = [] + for part in raw.split(separator: ",") { + if let tab = AppTab(rawValue: String(part)), seen.insert(tab).inserted { + result.append(tab) + } + } + for tab in AppTab.allCases where !seen.contains(tab) { + result.append(tab) + } + return result + } + @ViewBuilder var rootView: some View { switch self { diff --git a/myApp/HaruDanim/IOS/Views/HistoryView.swift b/myApp/HaruDanim/IOS/Views/HistoryView.swift index 1a5d46c..50c9179 100644 --- a/myApp/HaruDanim/IOS/Views/HistoryView.swift +++ b/myApp/HaruDanim/IOS/Views/HistoryView.swift @@ -293,8 +293,12 @@ struct HistoryView: View { } } - private func moveDay(_ delta: Int) { - selectedDayKey = math.calendar.date(byAdding: .day, value: delta, to: selectedDayKey)! + /// 날짜 이동 스텝은 보기 모드와 연동한다. + /// 타임테이블 '일주일' 보기에서는 한 번에 한 주(7일)씩 점프해 즉시 이전/다음 주가 보인다. + /// (목록·하루 타임테이블은 하루씩, 달력 팝업/‘오늘’로 날짜를 직접 선택하는 동작에는 영향 없음) + private func moveDay(_ direction: Int) { + let step = (mode == .timetable && timetableWeekly) ? 7 : 1 + selectedDayKey = math.calendar.date(byAdding: .day, value: direction * step, to: selectedDayKey)! } // MARK: 데이터 수집 (구간 겹침으로 하루 경계 분할 자동 반영) diff --git a/myApp/HaruDanim/IOS/Views/RadialNavigationView.swift b/myApp/HaruDanim/IOS/Views/RadialNavigationView.swift index d213387..19a9fba 100644 --- a/myApp/HaruDanim/IOS/Views/RadialNavigationView.swift +++ b/myApp/HaruDanim/IOS/Views/RadialNavigationView.swift @@ -65,6 +65,9 @@ struct RadialMenu: View { /// AppTab.rawValue (라우터의 tabSelection과 직접 연결) @Binding var selection: String + /// 사용자가 설정에서 지정한 반원 메뉴 아이콘 순서 (기기 로컬, 비동기화) + @AppStorage(SettingsKeys.radialTabOrder) private var radialOrderRaw = "" + // 레이아웃 상수 private let fabSize: CGFloat = 62 private let itemSize: CGFloat = 52 @@ -74,7 +77,8 @@ struct RadialMenu: View { /// 아이콘 원 중심에서 탭 이름 라벨 중심까지의 거리(원 아래로) private let labelOffset: CGFloat = 40 - private var tabs: [AppTab] { AppTab.allCases } + /// 왼쪽 끝 → 위 → 오른쪽 끝 순으로 배치할 탭 (설정의 커스텀 순서를 따른다) + private var tabs: [AppTab] { AppTab.radialOrder(from: radialOrderRaw) } private var currentTab: AppTab { AppTab(rawValue: selection) ?? .main diff --git a/myApp/HaruDanim/IOS/Views/SettingsView.swift b/myApp/HaruDanim/IOS/Views/SettingsView.swift index 658c2fb..3a18277 100644 --- a/myApp/HaruDanim/IOS/Views/SettingsView.swift +++ b/myApp/HaruDanim/IOS/Views/SettingsView.swift @@ -70,6 +70,14 @@ struct SettingsView: View { Text(style.label).tag(style.rawValue) } } + // 반원 메뉴일 때만 아이콘 배치 순서를 편집할 수 있다 + if navStyleRaw == NavStyle.radial.rawValue { + NavigationLink { + RadialOrderView() + } label: { + Label("반원 메뉴 순서 편집", systemImage: "arrow.up.arrow.down") + } + } } header: { Text("내비게이션") } footer: { @@ -279,6 +287,42 @@ struct SettingsView: View { } } +// MARK: - 반원(플로팅) 메뉴 아이콘 순서 편집 (iPhone 반원 모드 전용) + +/// 반원 메뉴를 펼쳤을 때 왼쪽 끝 → 위 → 오른쪽 끝으로 배치될 탭 순서를 드래그로 바꾼다. +/// 순서는 기기 로컬(비동기화)로만 저장되며, iPad·Mac 사이드바 순서에는 영향을 주지 않는다. +struct RadialOrderView: View { + @AppStorage(SettingsKeys.radialTabOrder) private var radialOrderRaw = "" + @State private var order: [AppTab] = [] + + var body: some View { + List { + Section { + ForEach(order) { tab in + HStack(spacing: 12) { + Image(systemName: tab.symbol) + .foregroundStyle(AppTheme.green) + .frame(width: 28) + Text(tab.label) + } + } + .onMove { source, destination in + order.move(fromOffsets: source, toOffset: destination) + radialOrderRaw = order.map(\.rawValue).joined(separator: ",") + } + } footer: { + Text("반원 메뉴를 펼쳤을 때 왼쪽 끝에서 오른쪽 끝으로 이 순서대로 아이콘이 배치돼요. 손잡이를 끌어 순서를 바꾸세요.") + } + } + .environment(\.editMode, .constant(.active)) + .scrollContentBackground(.hidden) + .background(AppTheme.background) + .navigationTitle("반원 메뉴 순서") + .navigationBarTitleDisplayMode(.inline) + .onAppear { order = AppTab.radialOrder(from: radialOrderRaw) } + } +} + // MARK: - 프리미엄 안내 struct PremiumView: View { diff --git a/myApp/HaruDanim/Shared/Settings.swift b/myApp/HaruDanim/Shared/Settings.swift index 79f5edc..4ea76f6 100644 --- a/myApp/HaruDanim/Shared/Settings.swift +++ b/myApp/HaruDanim/Shared/Settings.swift @@ -29,6 +29,8 @@ enum SettingsKeys { static let goalCardStyle = "settings.goalCardStyle" /// iPhone 내비게이션 방식. NavStyle rawValue ("tabBar" | "radial"). 기기 로컬(비동기화) static let navStyle = "settings.navStyle" + /// 반원(플로팅) 메뉴에 펼칠 탭 순서. AppTab rawValue를 쉼표로 연결. 기기 로컬(비동기화) + static let radialTabOrder = "settings.radialTabOrder" } /// iPhone 전용 내비게이션 방식 (iPad·Mac 사이드바에는 적용되지 않음, 기기 로컬 설정)