2

I have a list that contains a refresh button that would refresh the content of that row when a user taps on that. My implementation works, but the list view will get reset and scrolled back to the top of the list.

I have reviewed the SO post here for a solution, but it seems that my implementation does not have any .id(UUID().uuidString) to begin with.

Code:

struct FavouritesListView: View {
    @StateObject private var viewModel = FavouritesListViewModel()
    
    var body: some View {
        List {
            ForEach(viewModel.favouriteBusStops) { busStop in
                Section {
                    FavouriteView(busStop: busStop)
                }
            }
        }
    }
}

struct FavouriteView: View {
    @StateObject private var viewModel: BusStopDetailViewModel
    var busStop: BusStopEntity
        
    init(busStop: BusStopEntity) {
        self.busStop = busStop
        _viewModel = StateObject(wrappedValue: BusStopDetailViewModel(busStop: busStop))
    }
    
    var body: some View {
        VStack(alignment: .leading) {
            HStack(spacing: 20) {
                //Other UI components
                
                //Refresh button
                Button {
                    viewModel.refresh(busStop: busStop)
                } label: {
                    Image(systemName: "arrow.clockwise")
                }
            }
            
            if viewModel.isLoading {
                CenteredCircularProgressView()
            } else {
                if let arrivalInfo = viewModel.arrivalInfo {
                    //Other UI components
                }
            }
        }
    }
}

class BusStopDetailViewModel: ObservableObject {
    @Published var arrivalInfo: BusArrivalInfo?
    @Published var isLoading = false
    
    init(busStop: BusStopEntity) {
        getArrivalInfo(busStop: busStop)
    }
    
    func getArrivalInfo(busStop: BusStopEntity) {
        isLoading = true
        
        APIClient.shared.getBusArrivalInfo(busStopCode: busStop.busStopCode) { result in
            DispatchQueue.main.async {
                switch result {
                    case .success(let arrivalInfo):
                        self.arrivalInfo = arrivalInfo
                    case .failure(let error):
                        print("Err", error.localizedDescription)
                }
                
                self.isLoading = false
            }
        }
    }
    
    func refresh(busStop: BusStopEntity) {
        getArrivalInfo(busStop: busStop)
    }
}

What I'm looking for is that only that row refreshes when the refresh button is tapped, and that the scrollView does not get reset to the top.

Koh
  • 2,687
  • 1
  • 22
  • 62
  • Try to make your `FavoriteView` equatable (see in https://stackoverflow.com/a/60483313/12299030) so it depends on `BusStopEntity` only and be the same for same `busStop` (by some identifier, etc.). – Asperi Jan 03 '22 at 14:35

1 Answers1

1

Try to make your FavoriteView equatable (see in stackoverflow.com/a/60483313/12299030) so it depends on BusStopEntity only and be the same for same busStop (by some identifier, etc.).

Asperi
  • 228,894
  • 20
  • 464
  • 690