From 2c94486d349af90a4127851e28a7bded101cdf33 Mon Sep 17 00:00:00 2001 From: opficdev Date: Wed, 6 May 2026 23:33:08 +0900 Subject: [PATCH 1/7] =?UTF-8?q?ui:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=EC=9D=84=20sceneWidth=EC=9D=B4=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Common/Component/LoginButton.swift | 43 ++++++++------------ DevLog/UI/Login/LoginView.swift | 3 -- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/DevLog/UI/Common/Component/LoginButton.swift b/DevLog/UI/Common/Component/LoginButton.swift index a0a6f2f3..a8bb2bc8 100644 --- a/DevLog/UI/Common/Component/LoginButton.swift +++ b/DevLog/UI/Common/Component/LoginButton.swift @@ -10,8 +10,8 @@ import SwiftUI struct LoginButton: View { @State private var logo: Image? @State private var text = "" - @State private var height = CGFloat.zero - let action: () -> Void + private let height = UIFont.preferredFont(forTextStyle: .body).lineHeight + private let action: () -> Void init( logo: Image? = nil, @@ -27,31 +27,24 @@ struct LoginButton: View { Button(action: { action() }) { - HStack { - Text(text) - .foregroundStyle(Color.primary) - .font(.system(size: height / 3)) - .frame(maxWidth: .infinity, maxHeight: .infinity) - } + Text(text) + .foregroundStyle(Color.primary) + .font(.system(.body)) } - .contentShape(RoundedRectangle(cornerRadius: height / 2)) - .overlay( - GeometryReader { proxy in - ZStack(alignment: .leading) { - RoundedRectangle(cornerRadius: height / 2) - .stroke(Color.gray, lineWidth: 1) - .onAppear { - height = proxy.size.height - } - if let logo = logo { - logo - .resizable() - .scaledToFit() - .frame(width: height / 2, height: height / 2) - .padding(.leading) - } + .frame(width: 300, height: height + 16) + .contentShape(.capsule) + .overlay { + ZStack(alignment: .leading) { + Capsule() + .stroke(Color.gray, lineWidth: 1) + if let logo = logo { + logo + .resizable() + .scaledToFit() + .frame(width: height, height: height) + .padding(.leading) } } - ) + } } } diff --git a/DevLog/UI/Login/LoginView.swift b/DevLog/UI/Login/LoginView.swift index 30fafdfd..52c89a4b 100644 --- a/DevLog/UI/Login/LoginView.swift +++ b/DevLog/UI/Login/LoginView.swift @@ -25,17 +25,14 @@ struct LoginView: View { LoginButton(logo: Image("Google"), text: String(localized: "login_google_sign_in")) { viewModel.send(.tapSignInButton(.google)) } - .frame(width: sceneWidth * 3 / 4, height: sceneWidth / 10) LoginButton(logo: Image("Github"), text: String(localized: "login_github_sign_in")) { viewModel.send(.tapSignInButton(.github)) } - .frame(width: sceneWidth * 3 / 4, height: sceneWidth / 10) LoginButton(logo: Image("Apple"), text: String(localized: "login_apple_sign_in")) { viewModel.send(.tapSignInButton(.apple)) } - .frame(width: sceneWidth * 3 / 4, height: sceneWidth / 10) } .padding(.bottom, 30) Text(String(localized: "login_terms_notice")) From 3c61728933b645bfd32f7e1880f7bf24636f725d Mon Sep 17 00:00:00 2001 From: opficdev Date: Wed, 6 May 2026 23:41:21 +0900 Subject: [PATCH 2/7] =?UTF-8?q?chore:=20iPad,=20macOS=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=8F=84=20=EC=96=B4=EC=85=8B=EC=9D=84=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/Resource/Assets.xcassets/Apple.imageset/Contents.json | 4 ++-- DevLog/Resource/Assets.xcassets/Github.imageset/Contents.json | 4 ++-- DevLog/Resource/Assets.xcassets/Google.imageset/Contents.json | 2 +- .../Resource/Assets.xcassets/Primary.imageset/Contents.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DevLog/Resource/Assets.xcassets/Apple.imageset/Contents.json b/DevLog/Resource/Assets.xcassets/Apple.imageset/Contents.json index 80ffdaaa..17f02871 100644 --- a/DevLog/Resource/Assets.xcassets/Apple.imageset/Contents.json +++ b/DevLog/Resource/Assets.xcassets/Apple.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "filename" : "Apple_black.png", - "idiom" : "iphone" + "idiom" : "universal" }, { "appearances" : [ @@ -12,7 +12,7 @@ } ], "filename" : "Apple_white.png", - "idiom" : "iphone" + "idiom" : "universal" } ], "info" : { diff --git a/DevLog/Resource/Assets.xcassets/Github.imageset/Contents.json b/DevLog/Resource/Assets.xcassets/Github.imageset/Contents.json index efbdb0d1..3f6384bd 100644 --- a/DevLog/Resource/Assets.xcassets/Github.imageset/Contents.json +++ b/DevLog/Resource/Assets.xcassets/Github.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "filename" : "Github_black.png", - "idiom" : "iphone" + "idiom" : "universal" }, { "appearances" : [ @@ -12,7 +12,7 @@ } ], "filename" : "Github_white.png", - "idiom" : "iphone" + "idiom" : "universal" } ], "info" : { diff --git a/DevLog/Resource/Assets.xcassets/Google.imageset/Contents.json b/DevLog/Resource/Assets.xcassets/Google.imageset/Contents.json index f87048f4..89ac1240 100644 --- a/DevLog/Resource/Assets.xcassets/Google.imageset/Contents.json +++ b/DevLog/Resource/Assets.xcassets/Google.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "filename" : "Google.png", - "idiom" : "iphone" + "idiom" : "universal" } ], "info" : { diff --git a/DevLog/Resource/Assets.xcassets/Primary.imageset/Contents.json b/DevLog/Resource/Assets.xcassets/Primary.imageset/Contents.json index d1e8f444..d35a699e 100644 --- a/DevLog/Resource/Assets.xcassets/Primary.imageset/Contents.json +++ b/DevLog/Resource/Assets.xcassets/Primary.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "filename" : "Primary_dark.png", - "idiom" : "iphone" + "idiom" : "universal" } ], "info" : { From d7140161aecb8badd57c1c2e07574cf7ecc2bd7a Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 7 May 2026 00:20:13 +0900 Subject: [PATCH 3/7] =?UTF-8?q?ui:=20HomeView=EC=97=90=EC=84=9C=20screenWi?= =?UTF-8?q?dth=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B5=9C=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Common/Component/WebItemRow.swift | 5 ++--- DevLog/UI/Home/HomeView.swift | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/DevLog/UI/Common/Component/WebItemRow.swift b/DevLog/UI/Common/Component/WebItemRow.swift index d05d4523..9d2effba 100644 --- a/DevLog/UI/Common/Component/WebItemRow.swift +++ b/DevLog/UI/Common/Component/WebItemRow.swift @@ -8,15 +8,14 @@ import SwiftUI struct WebItemRow: View { - @Environment(\.sceneWidth) private var sceneWidth - + private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize let item: WebPageItem let showsChevron: Bool var body: some View { HStack { thumbnail - .frame(width: sceneWidth * 0.08, height: sceneWidth * 0.08) + .frame(width: labelWidth, height: labelWidth) .clipShape(RoundedRectangle(cornerRadius: 10)) VStack(alignment: .leading) { diff --git a/DevLog/UI/Home/HomeView.swift b/DevLog/UI/Home/HomeView.swift index 9a704266..40906d86 100644 --- a/DevLog/UI/Home/HomeView.swift +++ b/DevLog/UI/Home/HomeView.swift @@ -9,9 +9,9 @@ import SwiftUI struct HomeView: View { @Environment(\.diContainer) var container: any DIContainer - @Environment(\.sceneWidth) var sceneWidth: CGFloat @State private var router = NavigationRouter() @State var viewModel: HomeViewModel + private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize var body: some View { NavigationStack(path: $router.path) { @@ -210,7 +210,7 @@ struct HomeView: View { } else { ForEach(viewModel.state.recentTodos, id: \.id) { todo in NavigationLink(value: Path.detail(todo.id)) { - RecentTodoRow(todo: todo, sceneWidth: sceneWidth) + RecentTodoRow(todo: todo) } } } @@ -370,7 +370,7 @@ struct HomeView: View { HStack { RoundedRectangle(cornerRadius: 8) .fill(imageColor) - .frame(width: sceneWidth * 0.08, height: sceneWidth * 0.08) + .frame(width: labelWidth, height: labelWidth) .overlay { Image(systemName: systemName) .foregroundStyle(Color.white) @@ -391,15 +391,15 @@ struct HomeView: View { } private struct RecentTodoRow: View { + private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize let todo: RecentTodoItem - let sceneWidth: CGFloat var body: some View { let category = TodoCategoryItem(from: todo.category) HStack(alignment: .top, spacing: 12) { RoundedRectangle(cornerRadius: 8) .fill(category.color) - .frame(width: sceneWidth * 0.08, height: sceneWidth * 0.08) + .frame(width: labelWidth, height: labelWidth) .overlay { Image(systemName: category.symbolName) .foregroundStyle(Color.white) From 8c5d55ed856e57bc406cb6402361f5003dbff381 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 7 May 2026 00:27:42 +0900 Subject: [PATCH 4/7] =?UTF-8?q?ui:=20scneWidth=20*=200.08=20=ED=98=95?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20largetitle=EC=9D=98=20pointSize=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Common/Component/TodoItemRow.swift | 4 ++-- DevLog/UI/PushNotification/PushNotificationListView.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DevLog/UI/Common/Component/TodoItemRow.swift b/DevLog/UI/Common/Component/TodoItemRow.swift index 7a878ca0..e0440bab 100644 --- a/DevLog/UI/Common/Component/TodoItemRow.swift +++ b/DevLog/UI/Common/Component/TodoItemRow.swift @@ -8,7 +8,7 @@ import SwiftUI struct TodoItemRow: View { - @Environment(\.sceneWidth) private var sceneWidth + private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize private let item: TodoListItem init(_ item: TodoListItem) { @@ -19,7 +19,7 @@ struct TodoItemRow: View { HStack { Image(systemName: "checkmark.circle") .resizable() - .frame(width: sceneWidth * 0.08, height: sceneWidth * 0.08) + .frame(width: labelWidth, height: labelWidth) .foregroundStyle(item.isCompleted ? .green : .secondary) VStack(alignment: .leading, spacing: 0) { HStack { diff --git a/DevLog/UI/PushNotification/PushNotificationListView.swift b/DevLog/UI/PushNotification/PushNotificationListView.swift index 593b463e..117fd6e9 100644 --- a/DevLog/UI/PushNotification/PushNotificationListView.swift +++ b/DevLog/UI/PushNotification/PushNotificationListView.swift @@ -10,12 +10,12 @@ import SwiftUI struct PushNotificationListView: View { @State private var router = NavigationRouter() @State var viewModel: PushNotificationListViewModel - @Environment(\.sceneWidth) private var sceneWidth @Environment(\.colorScheme) private var colorScheme @Environment(\.diContainer) private var container: DIContainer @ScaledMetric(relativeTo: .body) private var headerHeight = 41 @State private var headerOffset: CGFloat = 0 @State private var isScrollTrackingEnabled = false + private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize var body: some View { NavigationStack(path: $router.path) { @@ -266,7 +266,7 @@ struct PushNotificationListView: View { let todoCategoryItem = TodoCategoryItem(from: item.todoCategory) RoundedRectangle(cornerRadius: 8) .fill(todoCategoryItem.color) - .frame(width: sceneWidth * 0.08, height: sceneWidth * 0.08) + .frame(width: labelWidth, height: labelWidth) .overlay { Image(systemName: todoCategoryItem.symbolName) .foregroundStyle(Color.white) From c74c85f08a4192faab4938ed921fd5e72b2e6e7c Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 7 May 2026 00:27:50 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Search/SearchView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/DevLog/UI/Search/SearchView.swift b/DevLog/UI/Search/SearchView.swift index 2c63c3c5..8db33875 100644 --- a/DevLog/UI/Search/SearchView.swift +++ b/DevLog/UI/Search/SearchView.swift @@ -9,7 +9,6 @@ import SwiftUI struct SearchView: View { @Environment(\.dismiss) private var dismiss - @Environment(\.sceneWidth) private var sceneWidth @Environment(\.diContainer) private var container: DIContainer @State private var router = NavigationRouter() @State var viewModel: SearchViewModel From 77e4718b3b86b28ee4a23c31f61c74808c7e1472 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 7 May 2026 01:41:38 +0900 Subject: [PATCH 6/7] =?UTF-8?q?ui:=20=ED=94=84=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=ED=9E=88=ED=8A=B8=EB=A7=B5=20=EB=B6=80=EB=B6=84=EC=9D=84=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20ui=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Profile/HeatmapView.swift | 63 +++++++++++++++++------------ 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/DevLog/UI/Profile/HeatmapView.swift b/DevLog/UI/Profile/HeatmapView.swift index 617d11d5..c8861c88 100644 --- a/DevLog/UI/Profile/HeatmapView.swift +++ b/DevLog/UI/Profile/HeatmapView.swift @@ -8,8 +8,7 @@ import SwiftUI struct HeatmapView: View { - @Environment(\.safeAreaInsets) private var safeAreaInsets - @Environment(\.sceneWidth) private var sceneWidth + @State private var availableWidth = CGFloat.zero let quarter: HeatmapQuarter let selectedActivityKinds: Set let selectedDay: HeatmapDay? @@ -21,31 +20,32 @@ struct HeatmapView: View { weekCounts: quarter.months.map(\.weeks.count) ) - HStack(alignment: .top, spacing: layout.monthSpacing) { - ForEach(quarter.months) { month in - MonthCompactHeatmapView( - month: month, - maxCount: maxCount, - layout: layout, - selectedActivityKinds: selectedActivityKinds, - selectedDay: selectedDay, - onSelectDay: onSelectDay - ) + ScrollView(.horizontal) { + LazyHStack(alignment: .top, spacing: layout.monthSpacing) { + ForEach(quarter.months) { month in + MonthCompactHeatmapView( + month: month, + maxCount: maxCount, + layout: layout, + selectedActivityKinds: selectedActivityKinds, + selectedDay: selectedDay, + onSelectDay: onSelectDay + ) + } + } + .padding(.vertical, 2) + .frame(width: layout.contentWidth, alignment: .leading) + } + .scrollIndicators(.hidden) + .scrollDisabled(true) + .frame(maxWidth: .infinity, alignment: .leading) + .background { + GeometryReader { geometry in + Color.clear + .onAppear { updateAvailableWidth(geometry.size.width) } + .onChange(of: geometry.size.width) { updateAvailableWidth($1) } } } - .padding(.vertical, 2) - } - - private var availableWidth: CGFloat { - // ProfileView의 바깥 가로 패딩(16)과 히트맵 카드 내부 패딩(12)을 합한 값 - let horizontalPadding: CGFloat = 16 + 12 - return max( - 0, - sceneWidth - - safeAreaInsets.leading - - safeAreaInsets.trailing - - (horizontalPadding * 2) - ) } private var maxCount: Int { @@ -70,13 +70,22 @@ struct HeatmapView: View { } return value } + + private func updateAvailableWidth(_ width: CGFloat) { + if availableWidth != width { + availableWidth = width + } + } } private struct HeatmapLayout { + private static let minimumCellSize: CGFloat = 8 + private static let maximumCellSize: CGFloat = 22 let cellSize: CGFloat let cellSpacing: CGFloat = 4 let monthSpacing: CGFloat = 12 let monthTitleSpacing: CGFloat = 6 + let contentWidth: CGFloat init(availableWidth: CGFloat, weekCounts: [Int]) { let totalColumns = max(weekCounts.reduce(0, +), 1) @@ -85,7 +94,9 @@ private struct HeatmapLayout { } let fixedWidth = monthSpacing * CGFloat(max(weekCounts.count - 1, 0)) + cellSpacing * CGFloat(totalColumnSpacings) - cellSize = max(0, availableWidth - fixedWidth) / CGFloat(totalColumns) + let fittingCellSize = max(0, availableWidth - fixedWidth) / CGFloat(totalColumns) + cellSize = min(max(fittingCellSize, Self.minimumCellSize), Self.maximumCellSize) + contentWidth = cellSize * CGFloat(totalColumns) + fixedWidth } var cellCornerRadius: CGFloat { From 9551239b6989ffe2ec933f771047472e94e4acef Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 7 May 2026 10:08:07 +0900 Subject: [PATCH 7/7] =?UTF-8?q?refactor:=20ScaledMetric=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Common/Component/LoginButton.swift | 2 +- DevLog/UI/Common/Component/TodoItemRow.swift | 2 +- DevLog/UI/Common/Component/WebItemRow.swift | 2 +- DevLog/UI/Home/HomeView.swift | 4 ++-- DevLog/UI/PushNotification/PushNotificationListView.swift | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DevLog/UI/Common/Component/LoginButton.swift b/DevLog/UI/Common/Component/LoginButton.swift index a8bb2bc8..1f925003 100644 --- a/DevLog/UI/Common/Component/LoginButton.swift +++ b/DevLog/UI/Common/Component/LoginButton.swift @@ -10,7 +10,7 @@ import SwiftUI struct LoginButton: View { @State private var logo: Image? @State private var text = "" - private let height = UIFont.preferredFont(forTextStyle: .body).lineHeight + @ScaledMetric(relativeTo: .body) private var height = CGFloat(22) private let action: () -> Void init( diff --git a/DevLog/UI/Common/Component/TodoItemRow.swift b/DevLog/UI/Common/Component/TodoItemRow.swift index e0440bab..370025b5 100644 --- a/DevLog/UI/Common/Component/TodoItemRow.swift +++ b/DevLog/UI/Common/Component/TodoItemRow.swift @@ -8,7 +8,7 @@ import SwiftUI struct TodoItemRow: View { - private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize + @ScaledMetric(relativeTo: .largeTitle) private var labelWidth = CGFloat(34) private let item: TodoListItem init(_ item: TodoListItem) { diff --git a/DevLog/UI/Common/Component/WebItemRow.swift b/DevLog/UI/Common/Component/WebItemRow.swift index 9d2effba..e1119172 100644 --- a/DevLog/UI/Common/Component/WebItemRow.swift +++ b/DevLog/UI/Common/Component/WebItemRow.swift @@ -8,7 +8,7 @@ import SwiftUI struct WebItemRow: View { - private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize + @ScaledMetric(relativeTo: .largeTitle) private var labelWidth = CGFloat(34) let item: WebPageItem let showsChevron: Bool diff --git a/DevLog/UI/Home/HomeView.swift b/DevLog/UI/Home/HomeView.swift index 40906d86..64a6e655 100644 --- a/DevLog/UI/Home/HomeView.swift +++ b/DevLog/UI/Home/HomeView.swift @@ -11,7 +11,7 @@ struct HomeView: View { @Environment(\.diContainer) var container: any DIContainer @State private var router = NavigationRouter() @State var viewModel: HomeViewModel - private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize + @ScaledMetric(relativeTo: .largeTitle) private var labelWidth = CGFloat(34) var body: some View { NavigationStack(path: $router.path) { @@ -391,7 +391,7 @@ struct HomeView: View { } private struct RecentTodoRow: View { - private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize + @ScaledMetric(relativeTo: .largeTitle) private var labelWidth = CGFloat(34) let todo: RecentTodoItem var body: some View { diff --git a/DevLog/UI/PushNotification/PushNotificationListView.swift b/DevLog/UI/PushNotification/PushNotificationListView.swift index 117fd6e9..7b5b433a 100644 --- a/DevLog/UI/PushNotification/PushNotificationListView.swift +++ b/DevLog/UI/PushNotification/PushNotificationListView.swift @@ -15,7 +15,7 @@ struct PushNotificationListView: View { @ScaledMetric(relativeTo: .body) private var headerHeight = 41 @State private var headerOffset: CGFloat = 0 @State private var isScrollTrackingEnabled = false - private let labelWidth: CGFloat = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize + @ScaledMetric(relativeTo: .largeTitle) private var labelWidth = CGFloat(34) var body: some View { NavigationStack(path: $router.path) {