본문 바로가기
Project/SwiftUI 블록와이드

[SwiftUI Project] 정렬 기능 만들기 (시총, 가격순, 변동률순) with Combine

by iOS_woo 2022. 9. 19.

시총, 가격순, 변동률 순으로 정렬 버튼 만들기

이번에는 코인 리스트를 시총, 가격순, 변동률 순으로 정렬할 수 있는 버튼을 만들겠습니다. 

 

먼저 ViewModel에 enum과 @Published 프로퍼티를 만들어줍니다. 

@Published var sortOption: SortOption = .rank

enum SortOption {
        case rank, price, pricereversed, priceChangePercentage24H, priceChangePercentage24HReversed,
        holdings
    }

 

기존에 HomeView에 데이터를 업데이트해주던 addSubscribers 함수에 .combineLatest($sortOption)으로 구독자를 생성해주었습니다.

 init() {
        addSubscribers()
    }
    
    func addSubscribers() {
        
        // AllCoins Update
        dataService.$allCoins
            .combineLatest($sortOption) // Subcriber
            .map(sortCoins)
            .sink { [weak self] (returnedCoins) in
                self?.allCoins = returnedCoins
            }
            .store(in: &cancellables)
     }

 

이제 map(sortCoins) 함수를 작성해줍니다. 

sortOption의 case에 따라서 다르게 정렬된 어레이를 리턴해주는 코드에요. 

// Sort AllCoins
    private func sortCoins(coins: [CoinModel], sort: SortOption) -> [CoinModel] {
        switch sort {
        case .rank, .holdings:
            return coins.sorted(by: { $0.rank < $1.rank })
        case .price:
            return coins.sorted(by: { $0.currentPrice > $1.currentPrice })
        case .pricereversed:
            return coins.sorted(by: { $0.currentPrice < $1.currentPrice })
        case .priceChangePercentage24H:
            return coins.sorted(by: { $0.priceChangePercentage24H > $1.priceChangePercentage24H})
        case .priceChangePercentage24HReversed:
            return coins.sorted(by: { $0.priceChangePercentage24H < $1.priceChangePercentage24H})
        }
    }

 

조건에 따라서 다르게 정렬된 코인 리스트를 받는 코드 작성은 끝났습니다. 

 

이제 기능을 가진 버튼을 만들겠습니다. 

저는 구현할 버튼이 세 개 밖에 없기 때문에 각각 작성했습니다. 

 

만들어야 하는 기능은 탭하면 글자색과 Capsule이 바뀌고, ViewModel의 sortOption 값을 업데이트 해주는겁니다. 

 

HStack에 id를 부여한 것은 ScrollTo.. 기능 때문입니다.

아이폰 se같이 넓이가 작은 화면에서는 글이 중간에 잘려서 불편한데요. 

탭하면 요소의 왼쪽 혹은 오른쪽으로 이동해 요소 전체가 보일 수 있게 해줌으로써 해결할 수 있습니다 .

 

struct SortOptionBarView: View {
    @StateObject var viewModel: HomeViewModel
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 30) {
                    HStack(spacing: 0) {
                        Text("Market Cap")
                            .foregroundColor((viewModel.sortOption == .rank) ? Color.white : Color.theme.accent)
                    }
                    .id("MARKET_CAP")
                    .padding(.vertical, 3)
                    .padding(.horizontal, 10)
                    .background(
                        Capsule()
                            .fill(viewModel.sortOption == .rank ? Color.theme.sortOptionSelected : .clear)
                    )
                    .onTapGesture {
                        viewModel.sortOption = .rank
                        withAnimation(.easeInOut) {
                            proxy.scrollTo("MARKET_CAP", anchor: .topLeading)
                        }
                    }
                    
                    let iconSize = 13
                    
                    HStack(spacing: 0) {
                        Text("Price")
                            .foregroundColor((viewModel.sortOption == .price || viewModel.sortOption == .pricereversed) ? Color.white : Color.theme.accent)
                        Image(systemName: "arrow.down")
                            .font(.system(size: CGFloat(iconSize)))
                            .foregroundColor(viewModel.sortOption == .price ?  Color.white : viewModel.sortOption == .pricereversed ? Color.theme.background : Color.theme.accent)
                        Image(systemName: "arrow.up")
                            .font(.system(size: CGFloat(iconSize)))
                            .foregroundColor(viewModel.sortOption == .pricereversed ?  Color.white : viewModel.sortOption == .price ? Color.theme.background : Color.theme.accent)
                    }
                    .padding(.vertical, 3)
                    .padding(.leading, 10)
                    .padding(.trailing, 5)
                    .background(
                        Capsule()
                            .fill((viewModel.sortOption == .price || viewModel.sortOption == .pricereversed) ? Color.theme.sortOptionSelected : .clear)
                    )
                    .onTapGesture {
                        viewModel.sortOption = viewModel.sortOption == .price ? .pricereversed : .price
                    }
                    
                    HStack(spacing: 0) {
                        Text("24h Change")
                            .foregroundColor((viewModel.sortOption == .priceChangePercentage24H || viewModel.sortOption == .priceChangePercentage24HReversed) ? Color.white : Color.theme.accent)
                        Image(systemName: "arrow.down")
                            .font(.system(size: CGFloat(iconSize)))
                            .foregroundColor(viewModel.sortOption == .priceChangePercentage24H ?  Color.white : viewModel.sortOption == .priceChangePercentage24HReversed ? Color.theme.background : Color.theme.accent)
                        Image(systemName: "arrow.up")
                            .font(.system(size: CGFloat(iconSize)))
                            .foregroundColor(viewModel.sortOption == .priceChangePercentage24HReversed ?  Color.white : viewModel.sortOption == .priceChangePercentage24H ? Color.theme.background : Color.theme.accent)
                    }
                    .id("24H_CHANGE")
                    .padding(.vertical, 3)
                    .padding(.leading, 10)
                    .padding(.trailing, 5)
                    .background(
                        Capsule()
                            .fill((viewModel.sortOption == .priceChangePercentage24H || viewModel.sortOption == .priceChangePercentage24HReversed) ? Color.theme.sortOptionSelected : .clear)
                    )
                    .onTapGesture {
                        viewModel.sortOption = viewModel.sortOption == .priceChangePercentage24H ? .priceChangePercentage24HReversed : .priceChangePercentage24H
                        withAnimation(.easeInOut) {
                            proxy.scrollTo("24H_CHANGE", anchor: .topTrailing)
                        }
                    }
                }
                .font(.system(size: 15, weight: .bold))
                .foregroundColor(Color.theme.accent)
            }
            .padding()
        }
    }
}

struct SortOptionBarView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            SortOptionBarView(viewModel: HomeViewModel())
                .previewLayout(.sizeThatFits)
            SortOptionBarView(viewModel: HomeViewModel())
                .preferredColorScheme(.dark)
                .previewLayout(.sizeThatFits)
        }
    }
}

 

완성되었습니다! :) 

 

 

SortOptionBarView.switui 깃허브 보러가기:

 

GitHub - mwoo-git/SwiftUICoin: Cloning Binance Lite with SwiftUI, Combine, MVVM

Cloning Binance Lite with SwiftUI, Combine, MVVM. Contribute to mwoo-git/SwiftUICoin development by creating an account on GitHub.

github.com

HomeViewModel.swift 깃허브 보러가기: 

 

GitHub - mwoo-git/SwiftUICoin: Cloning Binance Lite with SwiftUI, Combine, MVVM

Cloning Binance Lite with SwiftUI, Combine, MVVM. Contribute to mwoo-git/SwiftUICoin development by creating an account on GitHub.

github.com

참고한 유튜브: 

 

댓글