我有一个简单的列表,我想在修改基础数据时重绘行(例如,单击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
// ...
}