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 갱신을 최소화하기 위해 사용 /// 현재 보여지는 코인 구독 최적화를 위한 채널 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) diff --git a/AIProject/iCo/Features/Search/SearchBarView.swift b/AIProject/iCo/Features/Search/SearchBarView.swift index f24c1fe2..4c39db64 100644 --- a/AIProject/iCo/Features/Search/SearchBarView.swift +++ b/AIProject/iCo/Features/Search/SearchBarView.swift @@ -11,56 +11,83 @@ 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(.system(size: 14)) - .focused($isFocused) - .onChange(of: isFocused) { - showCancel = isFocused - } - - if !searchText.isEmpty { - CircleDeleteButton(fontSize: 9) { - searchText = "" + 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) - } - - Button { + .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) + + Button { isFocused = false searchText = "" - } label: { - Text("취소") - .foregroundStyle(.iCoNegative) - .font(.system(size: 13)) + } label: { + Text("취소") + .foregroundStyle(.iCoNegative) + .font(.ico13) + } + .opacity(showCancel ? 1 : 0) + .frame(width: showCancel ? 40 : 0, alignment: .trailing) + .animation(.default, value: showCancel) } - .opacity(showCancel ? 1 : 0) - .frame(width: showCancel ? 40 : 0, alignment: .trailing) - .animation(nil, value: showCancel) } + .frame(height: containerHeight) + .onAppear { recomputeHeight() } + .onChange(of: dynamicTypeSize, { oldValue, newValue in + recomputeHeight() + }) .onTapGesture { isFocused = true } } } + +#Preview(traits: .sizeThatFitsLayout) { + @Previewable @State var searchText: String = "key" + SearchBarView(searchText: $searchText) + .padding() + .frame(width:.infinity, height: 100) +}