我有一个内容视图,其中正在使用ForEach显示项目列表
@ObservedObject var homeVM : HomeViewModel
var body: some View {
ScrollView (.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
Spacer().frame(width:8)
ForEach(homeVM.favoriteStores , id: \._id){ item in
StoreRowOneView(storeVM: item)
}
Spacer().frame(width:8)
}
}.frame(height: 100)
}
我的ObservableObject
包含
@Published var favoriteStores = [StoreViewModel]()
StoreViewModel
的定义如下
class StoreViewModel : ObservableObject {
@Published var store:ItemStore
init(store:ItemStore) {
self.store = store
}
var _id:String {
return store._id!
}
}
如果我直接填充数组,则可以正常工作,并且可以看到商店列表 但是,如果我在进行网络调用(后台作业)后填充了数组,则该视图没有收到有关更改的通知
StoreApi().getFavedStores(userId: userId) { (response) in
guard response != nil else {
self.errorMsg = "Something wrong"
self.error = true
return
}
self.favoriteStores = (response)!.map(StoreViewModel.init)
print("counnt favorite \(self.favoriteStores.count)")
}
但是奇怪的是: -如果我添加一个显示数组项计数的文本,则该视图将得到通知,并且一切正常
这就是我的意思:
@ObservedObject var homeVM : HomeViewModel
var body: some View {
ScrollView (.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
Spacer().frame(width:8)
ForEach(homeVM.favoriteStores , id: \._id){ item in
StoreRowOneView(storeVM: item)
}
Text("\(self.homeVM.favoriteStores.count)").frame(width: 100, height: 100)
Spacer().frame(width:8)
}
}.frame(height: 100)
}
对此有何解释?
答案 0 :(得分:2)
对于水平ScrollView,您需要固定其高度,或者只使用有条件的ScrollView,它将在someModels.count > 0
时显示
var body: some View {
ScrollView(.horizontal) {
HStack() {
ForEach(someModels, id: \.someParameter1) { someModel in
someView(someModel)
}
}.frame(height: someFixedHeight)
}
}
// or
var body: some View {
VStack {
if someModels.count > 0 {
ScrollView(.horizontal) {
HStack() {
ForEach(someModels, id: \.someParameter1) { someModel in
someView(someModel)
}
}
}
} else {
EmptyView()
// or other view
}
}
}
通过实现
正确符合哈希协议func hash(into hasher: inout Hasher) {
hasher.combine(someParameter1)
hasher.combine(someParameter2)
}
和
static func == (lhs: SomeModel, rhs: SomeModel) -> Bool {
lhs.someParameter1 == rls.someParameter1 && lhs.someParameter2 == rls.someParameter2
}
并遵循@Asperi建议使用模型中的正确ID,该ID必须对每个元素都是唯一的。
ForEach(someModels, id: \.someParameter1) { someModel in
someView(someModel)
}
ForEach
除了唯一ID之外,没有其他方法可以通知更新。
Text(someModels.count)
之所以有效,是因为它在更改计数时通知更改。
答案 1 :(得分:2)
分享一些可能有用的经验。
当我使用ScrollView + ForEach + fetch时,直到我通过其他方式触发视图刷新之前,什么都没有显示。当我提供固定数据而不进行提取时,它会正常显示。我通过为ScrollView指定.infinity宽度来解决它。
我的理论是,ForEach在获取完成时会重新渲染,但是ScrollView某种程度上不会调整其宽度以适合内容。
与您的情况类似。通过提供固定宽度的Text,ForEach可以在初始化期间计算其宽度,而ScrollView可以使用它来调整其宽度。但是在获取时,ForEach的初始内容宽度为0,ScrollView也是如此。提取完成后,ScrollView不会相应地更新其宽度。
为ScrollView提供一些初始宽度应该可以解决您的问题。
答案 2 :(得分:0)
我将从将结果转发到主队列开始,如下所示
DispatchQueue.main.async {
self.favoriteStores = (response)!.map(StoreViewModel.init)
}
下一步... ForEach
跟踪id
参数,因此您应确保以下结果具有不同的_id
,否则ForEach未标记为需要更新>
ForEach(homeVM.favoriteStores , id: \._id){ item in
StoreRowOneView(storeVM: item)
}
答案 3 :(得分:0)
什么是ItemStore?更重要的是
ForEach(homeVM.favoriteStores , id: \._id){ item in
StoreRowOneView(storeVM: item)
}
_id
是什么,我相信您的_id
每次都相同,因此不会更新列表。您必须放置id:
一些变量,以便在列表更改后重新呈现它。
更新:如果没有其他变量可以使用它的值作为参考
ForEach((0..<homeVM.favoriteStores.count)) { index in
StoreRowOneView(storeVM:: homeVM.favoriteStores[index])
}