SwiftUI更新列表行

时间:2020-11-07 22:33:57

标签: ios swift xcode swiftui

我有一个简单的列表,我想在修改基础数据时重绘行(例如,单击toggle-favorite按钮)。当所有内容都在一个结构中定义时,它工作正常:

struct VacancyListContainer: View {
    @Binding var isPresented: Bool
    @StateObject var dataSource = VacancyListDataSource()

    var body: some View {
        NavigationView {
            ScrollView {
                LazyVStack {
                    ForEach(dataSource.items) { item in
                        NavigationLink(destination: VacancyDetailView(item: item)) {
//                            VacancyRow(dataSource: dataSource, item: item).onAppear {
//                                self.dataSource.loadMoreContentIfNeeded(currentItem: item)
//                            }
                              Title(item.displayTitle)
                              Button(action: { self.dataSource.toggleFavorite(item: item) }) {
                                  Text(item.is_favorite ? "❤️" : "?")
                              }
                            }
                            .onAppear {                                self.dataSource.loadMoreContentIfNeeded(currentItem: item)
                            }
                        }
                    }
                }
            }
        }
    }
}

但是,当我提取行视图时,重绘不再起作用。我猜发生这种情况是因为复制了item,但是我希望行视图仍会更新,因为列表已被修改:

struct VacancyRow: View {
    let dataSource: VacancyListDataSource?
    let item: JSON_Vacancy

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
                Title(item.displayTitle)
                Button(action: {
                        self.dataSource?.toggleFavorite(item: item)
                    }) {
                        Text(item.is_favorite ? "❤️" : "?")
                            .padding()
                    }
            }
        }
    }
}

数据源如下:

import Combine
import Foundation

class VacancyListDataSource: ObservableObject {
    @Published private(set) var items = [JSON_Vacancy]()
    @Published var isLoadingPage = false

    private var currentPage = 1
    private var hasNext = true
    private var subscriptions = Set<AnyCancellable>()

    init() {
        loadMoreContent()
    }

    func loadMoreContentIfNeeded(currentItem item: JSON_Vacancy?) {
        guard let item = item else {
            loadMoreContent()
            return
        }

        let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
        if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
            loadMoreContent()
        }
    }

    func toggleFavorite(item: JSON_Vacancy) {
        DriverAPI.toggle_favorite(item: item)
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    break
                case .failure:
                    break
                }
            }, receiveValue: { json in
                let updated = json.result
                if let index = self.items.firstIndex(of: item) {
                    self.items[index] = updated
                }
            })
            .store(in: &subscriptions)
    }

    private func loadMoreContent() {
        guard !isLoadingPage, hasNext else {
            return
        }

        isLoadingPage = true

        DriverAPI.vacancies(page: currentPage)
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    break
                case .failure:
                    self.hasNext = false
                }
            }, receiveValue: { json in
                self.hasNext = json.result.pager.next != nil
                self.isLoadingPage = false
                self.currentPage += 1
                self.items += json.result.objects
            })
            .store(in: &subscriptions)
    }

    func refresh() {
        items.removeAll()
        currentPage = 1
        hasNext = true
        loadMoreContent()
    }
}

提取VacancyRow并传递item以便在修改列表项时更新视图的正确方法是什么?


更新

尽我所能。可行,但对我来说真的很笨拙。也许有更好的选择,因为现在刷新列表时崩溃了吗?

// ...
VacancyRow(dataSource: dataSource, item: self.$dataSource.items[self.dataSource.items.firstIndex(of: item)!]).onAppear {
    self.dataSource.loadMoreContentIfNeeded(currentItem: item)
}
// ...
struct VacancyRow: View {
    let dataSource: VacancyListDataSource?
    @Binding var item: JSON_Vacancy

//  ...
}

0 个答案:

没有答案