From 042d37d427645a52e213f872e4f0788240364ee2 Mon Sep 17 00:00:00 2001 From: giljihun Date: Sun, 1 Feb 2026 20:13:09 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20[=EB=B3=B4=EA=B4=80=ED=95=A8]=20?= =?UTF-8?q?=ED=83=AD=20=EC=9E=AC=EC=84=A0=ED=83=9D=20=EC=8B=9C,=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=B5=9C=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Main/CollectionView+NormalMode.swift | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/Keychy/Keychy/Presentation/Collection/Views/Main/CollectionView+NormalMode.swift b/Keychy/Keychy/Presentation/Collection/Views/Main/CollectionView+NormalMode.swift index 124c7abf..5d4d9b7a 100644 --- a/Keychy/Keychy/Presentation/Collection/Views/Main/CollectionView+NormalMode.swift +++ b/Keychy/Keychy/Presentation/Collection/Views/Main/CollectionView+NormalMode.swift @@ -9,6 +9,13 @@ import SwiftUI // MARK: - Normal Mode View extension CollectionView { + /// 오버레이 헤더 높이 (headerSection + tagSection + collectionHeader) + /// - headerSection: 60(top padding) + ~40(buttons) + 2(padding) ≈ 102pt + /// - tagSection: 4(Spacing.xs) + 35(TabBar) ≈ 39pt + /// - collectionHeader: ~35pt (sortButton + spacing) + /// - 총합: ~185pt + private var overlayHeaderHeight: CGFloat { 185 } + // MARK: - Normal Mode View var normalModeView: some View { Group { @@ -44,16 +51,26 @@ extension CollectionView { } } } else { - // 정상 상태: 기존 VStack 형태 - VStack { - headerSection - .padding(.horizontal, Spacing.margin) - .padding(.top, 2) + // 정상 상태: ZStack 오버레이 형태 (iOS 빌트인 탭 스크롤 지원) + ZStack(alignment: .top) { + // 전체 화면 ScrollView (pullToRefresh가 생성) + normalCollectionSection - tagSection - .padding(.horizontal, Spacing.xs) + // 고정 오버레이 헤더 + VStack(spacing: 0) { + headerSection + .padding(.horizontal, Spacing.margin) + .padding(.top, 2) - normalCollectionSection + tagSection + .padding(.horizontal, Spacing.xs) + + collectionHeader + .padding(.horizontal, Spacing.padding) + .padding(.top, 10) + .padding(.bottom, 12) + } + .background(Color.white) } .contentShape(Rectangle()) .onTapGesture { @@ -147,35 +164,37 @@ extension CollectionView { } private var normalCollectionSection: some View { - VStack(spacing: 10) { - collectionHeader - .padding(.horizontal, Spacing.padding) + VStack(spacing: 0) { + // 오버레이 헤더 높이만큼 상단 여백 + Spacer() + .frame(height: overlayHeaderHeight) if filteredKeyrings.isEmpty { emptyView } else { collectionGridView(keyrings: filteredKeyrings) .padding(.horizontal, Spacing.xs) - .pullToRefresh(topPadding: 0) { - try? await Task.sleep(for: .seconds(1)) - fetchUserData() - retryFailedCaches() - } - .simultaneousGesture( - DragGesture().onChanged { _ in - if showSearchBar { - isSearchFieldFocused = false - - if !isSearching { - withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) { - showSearchBar = false - } - } - } - } - ) + .padding(.top, 20) } } + .pullToRefresh(topPadding: overlayHeaderHeight) { + try? await Task.sleep(for: .seconds(1)) + fetchUserData() + retryFailedCaches() + } + .simultaneousGesture( + DragGesture().onChanged { _ in + if showSearchBar { + isSearchFieldFocused = false + + if !isSearching { + withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) { + showSearchBar = false + } + } + } + } + ) } var collectionHeader: some View { From 56fb813bbd38bc19a650b9e0c6d01177ed8bf7c7 Mon Sep 17 00:00:00 2001 From: giljihun Date: Sun, 1 Feb 2026 20:14:42 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20pullToRefresh=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 보관함 뷰레이어 수정으로 인한 높이 값 수정 --- Keychy/Keychy/Core/Extensions/View+PullToRefresh.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Keychy/Keychy/Core/Extensions/View+PullToRefresh.swift b/Keychy/Keychy/Core/Extensions/View+PullToRefresh.swift index 6932cfa5..53a7e04b 100644 --- a/Keychy/Keychy/Core/Extensions/View+PullToRefresh.swift +++ b/Keychy/Keychy/Core/Extensions/View+PullToRefresh.swift @@ -73,10 +73,10 @@ struct PullToRefreshModifier: ViewModifier { } /// Refresh 중 content를 밀어내는 Spacer - /// - Refresh 중: topPadding이 있으면 topPadding, 없으면 60px (일정한 간격 유지) + /// - Refresh 중: 항상 60px (일정한 간격 유지) private var contentSpacer: some View { Spacer() - .frame(height: shouldHoldIndicator ? max(topPadding, 60) : min(pullDistance * 0.3, 60)) + .frame(height: shouldHoldIndicator ? 50 : min(pullDistance * 0.3, 50)) } /// Pull to Refresh Indicator @@ -90,7 +90,7 @@ struct PullToRefreshModifier: ViewModifier { isRefreshing: isRefreshing ) .allowsHitTesting(false) - .padding(.top, shouldHoldIndicator ? (topPadding == 0 ? 20 : topPadding + 40) : topPadding - 40) + .padding(.top, shouldHoldIndicator ? (topPadding == 0 ? 20 : topPadding + 20) : topPadding - 40) } /// 드래그 제스처 @@ -163,7 +163,7 @@ struct PullToRefreshModifier: ViewModifier { } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - withAnimation(.spring(response: 0.4, dampingFraction: 1.0)) { + withAnimation(.easeOut(duration: 0.25)) { shouldHoldIndicator = false indicatorOpacity = 0 } From 8ace81319401702f324c6ff4ad718c5f6f8a072b Mon Sep 17 00:00:00 2001 From: giljihun Date: Sun, 1 Feb 2026 20:21:52 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=EC=8B=9C=ED=81=90=EB=A6=AC=ED=8B=B0?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=20=ED=95=B4=EA=B2=B0=20(=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- functions/package-lock.json | 14 +++++++------- functions/package.json | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/functions/package-lock.json b/functions/package-lock.json index bb1275ec..72632e41 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -3300,9 +3300,9 @@ "peer": true }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz", + "integrity": "sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==", "funding": [ { "type": "github", @@ -3312,7 +3312,7 @@ "license": "MIT", "optional": true, "dependencies": { - "strnum": "^1.1.1" + "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -6561,9 +6561,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", "funding": [ { "type": "github", diff --git a/functions/package.json b/functions/package.json index 7ae822f7..84abd144 100644 --- a/functions/package.json +++ b/functions/package.json @@ -1,7 +1,8 @@ { "name": "functions", "overrides": { - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "fast-xml-parser": "^5.3.4" }, "scripts": { "build": "tsc",