From c85b82221ad533e71287fd14edd98d3be492dc78 Mon Sep 17 00:00:00 2001 From: kangho Date: Mon, 27 Oct 2025 12:11:03 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20dynamic=20font=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Features/Market/SubView/CoinCell.swift | 20 ++++++++----------- .../Market/SubView/HeaderToggleButton.swift | 4 ++-- .../SubView/RecentCoinSectionView.swift | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/AIProject/iCo/Features/Market/SubView/CoinCell.swift b/AIProject/iCo/Features/Market/SubView/CoinCell.swift index c31412b6..8ff357ca 100644 --- a/AIProject/iCo/Features/Market/SubView/CoinCell.swift +++ b/AIProject/iCo/Features/Market/SubView/CoinCell.swift @@ -43,18 +43,15 @@ fileprivate struct CoinMetaView: View { CoinView(symbol: symbol, size: 30) VStack(alignment: .leading, spacing: 6) { - Text(name.highlighted(searchTerm, font: .system(size: name.count < 8 ? 14 : 12, weight: .bold))) + Text(name.highlighted(searchTerm, font: name.count < 8 ? .ico14B : .ico12B)) .lineLimit(1) - .font(.system(size: name.count < 8 ? 14 : 12)) - .fontWeight(.bold) + .font(name.count < 8 ? .ico14B : .ico12B) Text(symbol.highlighted(searchTerm)) - .font(.system(size: 12)) .foregroundStyle(.secondary) } } - .font(.system(size: 12)) - .fontWeight(.medium) + .font(.ico11M) .foregroundStyle(.iCoLabel) } } @@ -92,31 +89,30 @@ fileprivate struct CoinPriceView: View { Text(ticker.snapshot.formatedPrice) .frame(minWidth: priceWidth, alignment: .trailing) - .font(.system(size: 15)) + .font(.ico15M) .blinkUnderlineOnChange(ticker.snapshot.price) } .frame(alignment: .trailing) HStack(spacing: 0) { Text("거래") - .font(.system(size: 11)) + .font(.ico11M) Text(ticker.snapshot.formatedVolume) .frame(minWidth: volumeWidth, alignment: .trailing) } } - .font(.system(size: 12)) - .fontWeight(.medium) + .font(.ico12M) .foregroundStyle(.iCoLabel) .background { VStack { ZStack { Text(ticker.snapshot.formatedPrice) - .font(.system(size: 15)) + .font(.ico15M) .measureWidth { w in priceWidth = w + pricePadding } Text(ticker.snapshot.formatedVolume) - .font(.system(size: 11)) + .font(.ico11M) .measureWidth { w in volumeWidth = w + pricePadding } diff --git a/AIProject/iCo/Features/Market/SubView/HeaderToggleButton.swift b/AIProject/iCo/Features/Market/SubView/HeaderToggleButton.swift index f2c733f7..a73000a7 100644 --- a/AIProject/iCo/Features/Market/SubView/HeaderToggleButton.swift +++ b/AIProject/iCo/Features/Market/SubView/HeaderToggleButton.swift @@ -21,12 +21,12 @@ struct HeaderToggleButton: View { } label: { HStack { Text(title) - .font(.system(size: 11)) + .font(.ico11) .foregroundStyle(.iCoLabelSecondary) HStack(spacing: 4) { Image(systemName: sortOrder.iconName) - .font(.system(size: 12, weight: .regular)) + .font(.ico12) .foregroundStyle(.iCoLabelSecondary) .animation(nil, value: sortOrder) } diff --git a/AIProject/iCo/Features/Market/SubView/RecentCoinSectionView.swift b/AIProject/iCo/Features/Market/SubView/RecentCoinSectionView.swift index 64d95589..f7bf0789 100644 --- a/AIProject/iCo/Features/Market/SubView/RecentCoinSectionView.swift +++ b/AIProject/iCo/Features/Market/SubView/RecentCoinSectionView.swift @@ -18,7 +18,7 @@ struct RecentCoinSectionView: View { ForEach(coins) { coin in HStack(spacing: 8) { Text(coin.koreanName) - .font(.system(size: 14)) + .font(.ico14) Button { deleteAction(coin) From 598dba515beb0dd440bee0bc5a4345a3628b3d05 Mon Sep 17 00:00:00 2001 From: kangho Date: Tue, 28 Oct 2025 11:57:50 +0900 Subject: [PATCH 2/4] feat: searchbar animation --- .../iCo/Features/Search/SearchBarView.swift | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/AIProject/iCo/Features/Search/SearchBarView.swift b/AIProject/iCo/Features/Search/SearchBarView.swift index f24c1fe2..d4b471a8 100644 --- a/AIProject/iCo/Features/Search/SearchBarView.swift +++ b/AIProject/iCo/Features/Search/SearchBarView.swift @@ -11,27 +11,28 @@ struct SearchBarView: View { @Binding var searchText: String @FocusState private var isFocused: Bool @State private var showCancel: Bool = false - + var body: some View { HStack(spacing: 0) { HStack { Image(systemName: "magnifyingglass") .foregroundStyle(.iCoLabel) - + TextField("코인 이름으로 검색하세요", text: $searchText) .keyboardType(.webSearch) .textInputAutocapitalization(.never) .autocorrectionDisabled(true) .padding(.horizontal, 8) .submitLabel(.search) - .font(.system(size: 14)) + .font(.ico14) .focused($isFocused) .onChange(of: isFocused) { showCancel = isFocused } - + if !searchText.isEmpty { CircleDeleteButton(fontSize: 9) { + print("Tapped") searchText = "" } } @@ -48,19 +49,25 @@ struct SearchBarView: View { } Button { - isFocused = false - searchText = "" + isFocused = false + searchText = "" } label: { Text("취소") .foregroundStyle(.iCoNegative) - .font(.system(size: 13)) + .font(.ico13) } .opacity(showCancel ? 1 : 0) .frame(width: showCancel ? 40 : 0, alignment: .trailing) - .animation(nil, value: showCancel) } .onTapGesture { isFocused = true } } } + +#Preview(traits: .sizeThatFitsLayout) { + @Previewable @State var searchText: String = "key" + SearchBarView(searchText: $searchText) + .padding() + .frame(width:.infinity, height: 100) +} From 0494273ddaf8a00df5f7ada5ad57c7e4f2f681c3 Mon Sep 17 00:00:00 2001 From: kangho Date: Mon, 3 Nov 2025 16:38:39 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20published=20=EB=AF=B8=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AIProject/iCo/Features/Market/MarketStore.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AIProject/iCo/Features/Market/MarketStore.swift b/AIProject/iCo/Features/Market/MarketStore.swift index bb8597f8..cbb0e85f 100644 --- a/AIProject/iCo/Features/Market/MarketStore.swift +++ b/AIProject/iCo/Features/Market/MarketStore.swift @@ -59,7 +59,7 @@ final class MarketStore: ObservableObject { } } - var sortCategory: Market.SortCategory = .volume { + @Published var sortCategory: Market.SortCategory = .volume { didSet { Task { await sortChannel.send(()) @@ -67,7 +67,7 @@ final class MarketStore: ObservableObject { } } - var volumeSortOrder: SortOrder = .descending { + @Published var volumeSortOrder: SortOrder = .descending { didSet { Task { await sortChannel.send(()) @@ -75,7 +75,7 @@ final class MarketStore: ObservableObject { } } - var rateSortOrder: SortOrder = .none { + @Published var rateSortOrder: SortOrder = .none { didSet { Task { await sortChannel.send(()) @@ -83,7 +83,7 @@ final class MarketStore: ObservableObject { } } - var filter: CoinFilter = .none { + @Published var filter: CoinFilter = .none { didSet { Task { await sortChannel.send(()) @@ -91,7 +91,7 @@ final class MarketStore: ObservableObject { } } - var sortedCoinIDs: [CoinID] = [] + @Published var sortedCoinIDs: [CoinID] = [] /// 아래 멤버들은 View 갱신을 최소화하기 위해 사용 /// 현재 보여지는 코인 구독 최적화를 위한 채널 From 01136d2b8cdfce8c93211608e54509a5e1debc68 Mon Sep 17 00:00:00 2001 From: kangho Date: Mon, 3 Nov 2025 16:38:56 +0900 Subject: [PATCH 4/4] feat: searchbar animation --- .../iCo/Features/Search/SearchBarView.swift | 102 +++++++++++------- 1 file changed, 61 insertions(+), 41 deletions(-) diff --git a/AIProject/iCo/Features/Search/SearchBarView.swift b/AIProject/iCo/Features/Search/SearchBarView.swift index d4b471a8..4c39db64 100644 --- a/AIProject/iCo/Features/Search/SearchBarView.swift +++ b/AIProject/iCo/Features/Search/SearchBarView.swift @@ -11,54 +11,74 @@ struct SearchBarView: View { @Binding var searchText: String @FocusState private var isFocused: Bool @State private var showCancel: Bool = false + @State private var containerHeight: CGFloat = 50 + @Environment(\.dynamicTypeSize) private var dynamicTypeSize + + private func recomputeHeight() { + // Use a Dynamic Type–aware font close to .ico14 (subheadline ~ 15pt) + let lineHeight = UIFont.preferredFont(forTextStyle: .subheadline).lineHeight + // Vertical paddings used below are 14(top)+14(bottom) = 28 + let calculated = lineHeight + 28 + // Ensure minimum tap target + containerHeight = max(44, calculated) + } var body: some View { - HStack(spacing: 0) { - HStack { - Image(systemName: "magnifyingglass") - .foregroundStyle(.iCoLabel) - - TextField("코인 이름으로 검색하세요", text: $searchText) - .keyboardType(.webSearch) - .textInputAutocapitalization(.never) - .autocorrectionDisabled(true) - .padding(.horizontal, 8) - .submitLabel(.search) - .font(.ico14) - .focused($isFocused) - .onChange(of: isFocused) { - showCancel = isFocused + GeometryReader { proxy in + HStack(spacing: 0) { + HStack { + Image(systemName: "magnifyingglass") + .foregroundStyle(.iCoLabel) + + TextField("코인 이름으로 검색하세요", text: $searchText) + .keyboardType(.webSearch) + .textInputAutocapitalization(.never) + .autocorrectionDisabled(true) + .padding(.horizontal, 8) + .submitLabel(.search) + .font(.ico14) + .focused($isFocused) + .onChange(of: isFocused) { + showCancel = isFocused + } + + if !searchText.isEmpty { + CircleDeleteButton(fontSize: 9) { + print("Tapped") + searchText = "" + } } + } + .padding(.horizontal, 12) + .padding(.vertical, 14) + .background { + RoundedRectangle(cornerRadius: 15) + .fill(showCancel ? .iCoBackgroundBlue : .iCoBackground) + } + .overlay { + RoundedRectangle(cornerRadius: 15) + .strokeBorder(showCancel ? .accentGradient : .defaultGradient, lineWidth: 0.5) + } + .animation(.bouncy, value: showCancel) - if !searchText.isEmpty { - CircleDeleteButton(fontSize: 9) { - print("Tapped") - searchText = "" - } + Button { + isFocused = false + searchText = "" + } label: { + Text("취소") + .foregroundStyle(.iCoNegative) + .font(.ico13) } + .opacity(showCancel ? 1 : 0) + .frame(width: showCancel ? 40 : 0, alignment: .trailing) + .animation(.default, value: showCancel) } - .padding(.horizontal, 12) - .padding(.vertical, 14) - .background { - RoundedRectangle(cornerRadius: 15) - .fill(showCancel ? .iCoBackgroundBlue : .iCoBackground) - } - .overlay { - RoundedRectangle(cornerRadius: 15) - .strokeBorder(showCancel ? .accentGradient : .defaultGradient, lineWidth: 0.5) - } - - Button { - isFocused = false - searchText = "" - } label: { - Text("취소") - .foregroundStyle(.iCoNegative) - .font(.ico13) - } - .opacity(showCancel ? 1 : 0) - .frame(width: showCancel ? 40 : 0, alignment: .trailing) } + .frame(height: containerHeight) + .onAppear { recomputeHeight() } + .onChange(of: dynamicTypeSize, { oldValue, newValue in + recomputeHeight() + }) .onTapGesture { isFocused = true }