简体   繁体   English

当 ObservedObject 更改时,SwiftUI 视图不会更新

[英]SwiftUI View does not updated when ObservedObject changed

I have a content view where i'm showing a list of items using ForEach我有一个内容视图,其中使用 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)
}

And my ObservableObject contains我的ObservableObject包含

@Published var favoriteStores = [StoreViewModel]()

And StoreViewModel is defined as below StoreViewModel定义如下

class StoreViewModel  : ObservableObject {
    @Published var store:ItemStore

    init(store:ItemStore) {
        self.store = store
    }

    var _id:String {
        return store._id!
    }
}

If i fill the array directly it work fine , and i can see the list of stores but if i filled the array after a network call (background job) , the view did not get notified that there is a change如果我直接填充数组,它工作正常,我可以看到商店列表,但如果我在网络调用(后台作业)后填充数组,则视图没有收到更改通知

 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)")
        }

But the weird thing is : - if i add a text which show the count of the array items , the view will get notified and everything work fine但奇怪的是: - 如果我添加一个显示数组项计数的文本,视图将收到通知并且一切正常

That's what i mean :那就是我的意思 :

    @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)
    }

Any explaining for that ?对此有任何解释吗?

For horizontal ScrollView you need to have your height fixed or just use conditional ScrollView which will show when someModels.count > 0对于水平 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
      }
   }
}

Properly conforming to Hashable protocol by implementing通过实现正确符合 Hashable 协议

func hash(into hasher: inout Hasher) { 
   hasher.combine(someParameter1)
   hasher.combine(someParameter2)
}

and

static func == (lhs: SomeModel, rhs: SomeModel) -> Bool {
   lhs.someParameter1 == rls.someParameter1 && lhs.someParameter2 == rls.someParameter2

}

And also following @Asperi suggestion of using proper id from model, which must be unique to every element.并且还遵循@Asperi 建议使用模型中的正确 id,它必须对每个元素都是唯一的。

ForEach(someModels, id: \.someParameter1) { someModel in
    someView(someModel)
}

There is no other way for ForEach to notify updates other than unique ids. ForEach没有其他方法可以通知唯一 ID 以外的更新。

Text(someModels.count) was working because it is notifying the changes as count is being changed. Text(someModels.count)正在工作,因为它会在更改计数时通知更改。

Share some experiences that might help.分享一些可能有帮助的经验。

When I used ScrollView + ForEach + fetch, nothing shows until I trigger view refresh via other means.当我使用 ScrollView + ForEach + fetch 时,在我通过其他方式触发视图刷新之前不会显示任何内容。 It shows normally when I supply fixed data without fetching.当我提供固定数据而不获取时,它会正常显示。 I solved it by specifying .infinity width for ScrollView.我通过为 ScrollView 指定 .infinity 宽度来解决它。

My theory is that ForEach re-renders when fetch completes, but ScrollView somehow does not adjust its width to fit the content.我的理论是 ForEach 在 fetch 完成时重新渲染,但 ScrollView 以某种方式不调整其宽度以适应内容。

It is similar in your case.你的情况类似。 By supplying fixed width Text, ForEach can calculate its width during init, and ScrollView can use that to adjust its width.通过提供固定宽度的 Text,ForEach 可以在 init 期间计算其宽度,而 ScrollView 可以使用它来调整其宽度。 But in case of fetch, ForEach has initial 0 content width, and so is ScrollView.但是在 fetch 的情况下,ForEach 的初始内容宽度为 0,ScrollView 也是如此。 When fetch completes ScrollView does not update its width accordingly.当获取完成时 ScrollView 不会相应地更新其宽度。

Giving ScrollView some initial width should solve your problem.给 ScrollView 一些初始宽度应该可以解决你的问题。

I would start from forwarding results to the main queue, as below我将从将结果转发到主队列开始,如下所示

DispatchQueue.main.async {
    self.favoriteStores = (response)!.map(StoreViewModel.init)
}

Next... ForEach tracks id parameters, so you should be sure that results have different _id s in the following, otherwise ForEach is not marked as needed to update接下来... ForEach跟踪id参数,所以你应该确保结果在下面有不同的_id ,否则 ForEach 没有标记为需要更新

ForEach(homeVM.favoriteStores , id: \._id){ item in
    StoreRowOneView(storeVM: item)
}

What is ItemStore?什么是物品商店? more importantly更重要的是

ForEach(homeVM.favoriteStores , id: \._id){ item in
    StoreRowOneView(storeVM: item)
}

what is _id i believe your _id is same each time so it will not update the list.什么是_id我相信你的_id每次都是一样的,所以它不会更新列表。 You have to put id: some variable that you want to re-render the list when it changed.你必须把id:一些变量,当它改变时你想重新呈现列表。

Update: If you do not have any other variable to take its value as reference than you can use更新:如果您没有任何其他变量将其值作为参考,则可以使用

ForEach((0..<homeVM.favoriteStores.count)) { index in
    StoreRowOneView(storeVM:: homeVM.favoriteStores[index])
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM