swiftuiswiftui-ontapgesturescrollviewreader

How to use ScrollViewReader in two List from two different api (without Button\TextField) using .onTapGesture in SwiftUI?


I have project, where two list of different banks show currency rate. And I want when I choose currency in first List, second List will have autoscrolling to the same currency. I saw tutorials with buttons, but it's not what I need. I thought use onTapGesture, but I can't realise how to involve it in right way. ExchangeRatesPB(ccy) and ExchangeRatesNBU(cc) have the same short name of some currencies: "USD", "EUR", "RUR". Thanks for help anyway.

struct ContentView: View {
        @State private var date = Date()
        @State var currenciesPB: [ExchangeRatesPB] = []
        @State var currenciesNBU: [ExchangeRatesNBU] = []
        
        @State var scrollToCurrency: String = ""
        
        var body: some View {
            ZStack {
                Color(.systemGray6)
                    .edgesIgnoringSafeArea(.all)
                VStack {
                    HStack {
                        Spacer(minLength: 110)
                        Text("Exchange rates")
                            .font(.title2)
                            .padding()
                        
                        Spacer()
                        Image(systemName: "chart.bar.xaxis")
                            .font(.title)
                            .padding()
                    }
                    .foregroundColor(.white)
                    .padding(.top)
                    .background(Color.init(#colorLiteral(red: 0.4558259845, green: 0.5886077285, blue: 0.5515387654, alpha: 1)))
                    .edgesIgnoringSafeArea(.top)
                    
                    //PB
                    VStack {
                        HStack {
                            Text("PrivatBank")
                                .foregroundColor(Color.init(#colorLiteral(red: 0.3549223542, green: 0.3776315749, blue: 0.4196012616, alpha: 1)))
                            Spacer(minLength: 100)
                            
                            Image(systemName: "calendar")
                                .foregroundColor(.gray)
                            DatePicker("", selection: $date, displayedComponents: .date)
                        }
                        .font(.title2)
                        .padding(10)
                        
                        HStack(spacing: 60) {
                            Text("Currency")
                            Text("Purchase")
                            Text("Sale")
                        } .foregroundColor(.gray)
                        VStack {
                            //                        ScrollView {
                            List(currenciesPB, id: \.self) { currencyPB in
                                VStack {
                                    HStack(spacing: 60) {
                                        Text(currencyPB.ccy)
                                            .id(currencyPB.ccy)
                                        Text(currencyPB.sale)
                                        Text(currencyPB.buy)
                                    }
                                   // .onTapGesture {
                                        //scrollToCurrency = currencyPB.ccy
                                        //print(scrollToCurrency)
                                   // }
                                    .foregroundColor(Color.init(#colorLiteral(red: 0.3549223542, green: 0.3776315749, blue: 0.4196012616, alpha: 1)))
                                }
                            }
                            //                        }
                            .padding(.top)
                            .onAppear() {
                                ApiPB().getCurrency { (currenciesPB) in
                                    self.currenciesPB = currenciesPB
                                }
                            }
                        }
                    }
                    
                    //NBU
                    VStack {
                        HStack {
                            Text("NBU")
                                .foregroundColor(Color.init(#colorLiteral(red: 0.3549223542, green: 0.3776315749, blue: 0.4196012616, alpha: 1)))
                            Spacer(minLength: 150)
                            
                            Image(systemName: "calendar")
                                .foregroundColor(.gray)
                            DatePicker("", selection: $date, displayedComponents: .date)
                        }
                        .font(.title2)
                        .padding(10)
                        //                    ScrollView {
                        ScrollViewReader { value in
                            VStack {
                                List(currenciesNBU, id: \.self) { currencyNBU in
                                    VStack {
                                        HStack(spacing: 50) {
                                            Text(currencyNBU.txt)
                                            Spacer()
                                            VStack {
                                                HStack {
                                                    Text(String(currencyNBU.rate))
                                                    Text("UAH")
                                                }
                                            }
                                        }
                                        .listRowBackground(Color(.systemGray6))
                                        .foregroundColor(Color.init(#colorLiteral(red: 0.3549223542, green: 0.3776315749, blue: 0.4196012616, alpha: 1)))
                                    }
                                }
                                .onChange(of: scrollToCurrency) { _ in
                                    value.scrollTo(scrollToCurrency, anchor: .top)
                                }
                                .padding(.top)
                                .onAppear() {
                                    ApiNBU().getCurrency { (currenciesNBU) in
                                        self.currenciesNBU = currenciesNBU
                                    }
                                }
                            }
                            
                        }
                        //                    }
                    }
                }
            }
        }
    }
    
    struct ExchangeRatesPB: Codable, Hashable {
        let ccy: String
        let base_ccy: String
        var buy: String
        var sale: String
    }
    
    class ApiPB {
        func getCurrency(completion: @escaping ([ExchangeRatesPB]) -> ()) {
            guard let url = URL(string: "https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5") else { return }
            
            URLSession.shared.dataTask(with: url) { (data, _, _) in
                let currenciesPB = try! JSONDecoder().decode([ExchangeRatesPB].self, from: data!)
                DispatchQueue.main.sync {
                    completion(currenciesPB)
                }
            }
            .resume()
        }
    }
    
    struct ExchangeRatesNBU: Codable, Hashable {
        let r030: Int
        let txt: String
        var rate: Double
        var cc: String
    }
    
    class ApiNBU {
        func getCurrency(completion: @escaping ([ExchangeRatesNBU]) -> ()) {
            guard let url = URL(string: "https://bank.gov.ua/NBUStatService/v1/statdirectory/exchange?date=20200302&json") else { return }
            URLSession.shared.dataTask(with: url) { (data, _, _) in
                let currenciesNBU = try! JSONDecoder().decode([ExchangeRatesNBU].self, from: data!)
                DispatchQueue.main.sync {
                    completion(currenciesNBU)
                }
            }
            .resume()
        }
    }

Solution

  • Your approach is totally fine, you missed just once thing. First argument in scrollTo() is id, and when you're creating your list you've specified self as id. So to scroll to that element you need to pass the whole object you wanna scroll to, or just change id for your list like this:

    List(currenciesNBU, id: \.cc)