From f5bb5825cb6c74ab2d3b5201dbb841f72ebee2a6 Mon Sep 17 00:00:00 2001 From: jihun Date: Tue, 2 Jun 2026 20:36:32 +0900 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=EC=9D=B8=EC=A6=9D=EC=83=B7=20?= =?UTF-8?q?=EC=83=81=ED=95=98=20=EC=8A=A4=EC=99=80=EC=9D=B4=ED=94=84=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Detail/GoalDetailView.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift index e329fdb3..f748f95f 100644 --- a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift +++ b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift @@ -188,8 +188,22 @@ private extension GoalDetailView { cardOffset = repeatedCardOffset(for: width) isCrossingDuringDrag = shouldCrossCards(for: width) } - .onEnded { _ in + .onEnded { value in guard !store.isEditing else { return } + + let translation = value.translation + let width = resistedDragWidth( + for: translation.width, + velocity: value.velocity.width + ) + + guard abs(width) >= abs(translation.height) else { + withAnimation(.spring(response: 0.2, dampingFraction: 0.94)) { + resetDragState() + } + return + } + withAnimation(.spring(response: 0.2, dampingFraction: 0.94)) { resetDragState() store.send(.view(.cardSwiped)) From 8c82d677e82c814f526fc05571d4143639e9ce24 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:06:56 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20disable=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=20?= =?UTF-8?q?-=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Button/Round/TXRoundButton.swift | 10 +++++++++- .../Sources/Components/Button/TXButton.swift | 4 ++++ .../Sources/Components/Card/Goal/GoalCardView.swift | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift index ea895461..0266469e 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift @@ -9,12 +9,20 @@ import SwiftUI struct TXRoundButton: View { let shape: TXButtonShape + let allowsActionWhenDisabled: Bool let onTap: () -> Void public var body: some View { if case let .round(style, size, state) = shape { Button { - onTap() + switch state { + case .disabled: + if allowsActionWhenDisabled { + onTap() + } + case .standard: + onTap() + } } label: { ZStack { Capsule() diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift index 6af90249..cf8d27e7 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/TXButton.swift @@ -24,6 +24,7 @@ import SwiftUI /// ``` public struct TXButton: View { let shape: TXButtonShape + let allowsActionWhenDisabled: Bool let onTap: () -> Void /// 버튼을 생성합니다. @@ -41,9 +42,11 @@ public struct TXButton: View { /// ``` public init( shape: TXButtonShape, + allowsActionWhenDisabled: Bool = false, onTap: @escaping () -> Void ) { self.shape = shape + self.allowsActionWhenDisabled = allowsActionWhenDisabled self.onTap = onTap } @@ -65,6 +68,7 @@ public struct TXButton: View { case .round: TXRoundButton( shape: shape, + allowsActionWhenDisabled: allowsActionWhenDisabled, onTap: onTap ) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift index 524cb235..3bddb7d2 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift @@ -169,6 +169,7 @@ private extension GoalCardView { size: .s, state: isButtonDisabled ? .disabled : .standard ), + allowsActionWhenDisabled: true, onTap: { buttonAction?() } ) .padding(.bottom, 14) From a3bb2ab60caf10ac5ef007ed80a63ec6e3dff083 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:09:28 +0900 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20dropdownButton=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A3=BC=EC=9E=85=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81=20-=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MakeGoal/Sources/MakeGoalView.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift b/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift index 147d9888..7f94a323 100644 --- a/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift +++ b/Projects/Feature/MakeGoal/Sources/MakeGoalView.swift @@ -177,7 +177,9 @@ private extension MakeGoalView { Spacer() if store.showPeriodCount { - dropDownButton { store.send(.view(.periodSelected)) } + dropDownButton(text: store.periodCountText) { + store.send(.view(.periodSelected)) + } } } } @@ -190,7 +192,9 @@ private extension MakeGoalView { Spacer() - dropDownButton { store.send(.view(.startDateTapped)) } + dropDownButton(text: store.startDateText) { + store.send(.view(.startDateTapped)) + } } .frame(height: 32) .padding(.vertical, 16) @@ -214,7 +218,9 @@ private extension MakeGoalView { Spacer() - dropDownButton { store.send(.view(.endDateTapped)) } + dropDownButton(text: store.endDateText) { + store.send(.view(.endDateTapped)) + } } .padding(.vertical, 21.5) } @@ -236,9 +242,12 @@ private extension MakeGoalView { .padding(.vertical, -1) } - func dropDownButton(_ action: @escaping () -> Void) -> some View { + func dropDownButton( + text: String, + action: @escaping () -> Void + ) -> some View { HStack(spacing: 0) { - Text(store.startDateText) + Text(text) .typography(.b2_14r) .foregroundStyle(Color.Gray.gray500) Image.Icon.Symbol.arrow2Down From c9ff5f47ff8783d4c6c5d0181ee8e2a852df3710 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:13:39 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=EC=9D=B8=EC=A6=9D=EC=83=B7=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=EB=9E=99=EC=85=98=20=EC=8B=9C=20=EB=93=9C?= =?UTF-8?q?=EB=9E=98=EA=B7=B8=20=EC=9C=84=EC=B9=98=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=BD=94=EB=A9=98=ED=8A=B8=20offset=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-?= =?UTF-8?q?=20#337?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift index f748f95f..75bd83c3 100644 --- a/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift +++ b/Projects/Feature/GoalDetail/Sources/Detail/GoalDetailView.swift @@ -445,7 +445,7 @@ private extension GoalDetailView { .padding(.bottom, 26) .frame(width: rectFrame.width, height: rectFrame.height, alignment: .bottom) .rotationEffect(frontCardRotation) - .offset(x: posX, y: posY - keyboardInset) + .offset(x: posX + cardOffset, y: posY - keyboardInset) .animation(.easeOut(duration: 0.25), value: keyboardInset) } } From 117147ac94f43e86a69efe4bf0dececd7d7e2709 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 14:31:20 +0900 Subject: [PATCH 5/7] =?UTF-8?q?fix:=20=ED=86=B5=EA=B3=84=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=84=A4=EB=B9=84=EB=B0=94=20=EC=95=88=EC=9E=98?= =?UTF-8?q?=EB=A6=AC=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-=20#338?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Feature/Stats/Sources/Detail/StatsDetailView.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift index 92f52b5c..0ce3b6df 100644 --- a/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift +++ b/Projects/Feature/Stats/Sources/Detail/StatsDetailView.swift @@ -156,8 +156,7 @@ private extension StatsDetailView { summaryTitle(for: summary.title) summartyContent(content: summary.content, isCompletedCount: summary.isCompletedCount) .layoutPriority(1) - - Spacer() + .frame(maxWidth: .infinity, alignment: .leading) } } } @@ -188,7 +187,6 @@ private extension StatsDetailView { Text(content[0]) .typography(.b4_12b) .foregroundStyle(Color.Gray.gray500) - .lineLimit(1) if isCompletedCount { Text("|") @@ -199,11 +197,9 @@ private extension StatsDetailView { Text(content[1]) .typography(.b4_12b) .foregroundStyle(Color.Gray.gray500) - .lineLimit(1) } } - .lineLimit(1) - .fixedSize(horizontal: true, vertical: false) + .frame(maxWidth: .infinity, alignment: .leading) } @ViewBuilder From b64e23e69819429fc9c5e54936aa12f1c5631938 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 20:10:23 +0900 Subject: [PATCH 6/7] =?UTF-8?q?fix:=20=ED=99=88=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=97=86=EC=9D=84=20=EB=95=8C=20UI=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?#339=20-=20emptyView=20y=EC=B6=95=20=EA=B0=80=EC=9A=B4=EB=8D=B0?= =?UTF-8?q?=20=EC=A0=95=EB=A0=AC=20-=20=EC=B2=AB=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20=ED=99=94=EC=82=B4=ED=91=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Home/HomeEmptyContentSection.swift | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift b/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift index 6212798f..e056ef9b 100644 --- a/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift +++ b/Projects/Feature/Home/Sources/Home/HomeEmptyContentSection.swift @@ -10,42 +10,34 @@ import FeatureHomeInterface import SharedDesignSystem /// `hadFirstGoal`을 읽는 빈 상태 영역입니다. -/// `emptyScrollHeight`는 로컬 `@State`로 관리해 다른 콘텐츠 section 재렌더링에 의해 -/// 초기화되지 않게 합니다. +/// `goalEmptyView`의 center anchor를 기기 화면 기준 y축 중앙에 배치합니다. struct HomeEmptyContentSection: View { let store: StoreOf - @State private var emptyScrollHeight: CGFloat = 0 - var body: some View { VStack(spacing: 0) { - HomeHeaderRow(store: store) - .padding(.horizontal, 20) - .padding(.top, 16) + GeometryReader { geo in + let frame = geo.frame(in: .global) + let deviceHeight = UIScreen.main.bounds.height + let deviceCenterYInSection = max(0, deviceHeight / 2 - frame.minY) - ScrollView { - goalEmptyView - // 실제 가시 영역 기준으로 중앙 정렬되도록 탭바 높이만큼 차감 - .frame(maxWidth: .infinity, minHeight: max(0, emptyScrollHeight - 58)) - .padding(.bottom, 58) - } - .scrollIndicators(.hidden) - .refreshable { - store.send(.view(.refreshPulled)) - } - .overlay(alignment: .bottomTrailing) { - emptyArrow - } - .frame(maxHeight: .infinity) - .background { - GeometryReader { geo in - Color.clear - .onAppear { emptyScrollHeight = geo.size.height } - .onChange(of: geo.size.height) { _, newValue in - emptyScrollHeight = newValue - } + ScrollView { + goalEmptyView + .frame(width: geo.size.width) + .position(x: geo.size.width / 2, y: deviceCenterYInSection) + } + .scrollIndicators(.hidden) + .refreshable { + store.send(.view(.refreshPulled)) } + .overlay(alignment: .bottomTrailing) { + if store.hadFirstGoal == false { + emptyArrow + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) } + .frame(maxHeight: .infinity) } } @@ -79,12 +71,11 @@ struct HomeEmptyContentSection: View { } } } - .frame(maxWidth: .infinity, maxHeight: .infinity) } var emptyArrow: some View { Image.Illustration.arrow - .padding(.bottom, 71 + 58) + .padding(.bottom, 71 + TXTabBarLayout.height) .padding(.trailing, 86) .ignoresSafeArea() } From c74ef33adb32cbe0c2409742e9770b4353d5d047 Mon Sep 17 00:00:00 2001 From: jihun Date: Thu, 4 Jun 2026 21:20:03 +0900 Subject: [PATCH 7/7] =?UTF-8?q?fix:=20TXRoundButton=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20UI=20=EC=88=98=EC=A0=95=20-=20#339?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Button/Round/TXRoundButton.swift | 28 ++++++------------- .../Components/Card/Goal/GoalCardView.swift | 2 +- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift index 0266469e..b72bdaf1 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Button/Round/TXRoundButton.swift @@ -24,12 +24,12 @@ struct TXRoundButton: View { onTap() } } label: { - ZStack { + ZStack(alignment: .top) { Capsule() .fill(style.backgroundColor(state: state)) .frame(maxWidth: size.frameWidth) - .frame(height: size.backgroundHeight(state: state)) - .padding(.top, size.bottomYOffset(state: state)) + .frame(height: size.backgroundHeight) + .padding(.top, size.backgroundYOffset) Text(style.text) .typography(size.typography) @@ -42,8 +42,8 @@ struct TXRoundButton: View { lineWidth: size.borderWidth ) .background(style.foregroundColor(state: state), in: .capsule) + .padding(.top, size.foregroundYOffset(state: state)) } - .padding(.top, size.topYOffset(state: state)) } .buttonStyle(.plain) } else { @@ -120,29 +120,19 @@ private extension TXButtonShape.TXRoundSize { } } - func backgroundHeight(state: TXButtonShape.TXRoundState) -> CGFloat { + var backgroundHeight: CGFloat { switch self { case .l, .m: 70 - case .s: - switch state { - case .standard: 31 - case .disabled: 28 - } + case .s: 28 } } - func bottomYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { - switch self { - case .s, .l, .m: - switch state { - case .standard: 4 - case .disabled: 1 - } - } + var backgroundYOffset: CGFloat { + 4 } - func topYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { + func foregroundYOffset(state: TXButtonShape.TXRoundState) -> CGFloat { switch self { case .s, .l, .m: switch state { diff --git a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift index 3bddb7d2..ae498178 100644 --- a/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift +++ b/Projects/Shared/DesignSystem/Sources/Components/Card/Goal/GoalCardView.swift @@ -180,7 +180,7 @@ private extension GoalCardView { .padding(.bottom, 10) } } - .frame(maxHeight: .infinity) + .frame(maxHeight: .infinity, alignment: .center) } func emojiImage(emoji: Image) -> some View {