如果数组是ObservableObject的成员,如何绑定数组和List? [英] How to bind an array and List if the array is a member of ObservableObject?

查看:78
本文介绍了如果数组是ObservableObject的成员,如何绑定数组和List?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个 MyViewModel ,它从网络获取数据,然后更新结果. MyView 应该订阅 $ model.results ,并显示 List 填充结果.

I want to create MyViewModel which gets data from network and then updates the arrray of results. MyView should subscribe to the $model.results and show List filled with the results.

不幸的是,我收到一个有关表达式类型在没有更多上下文的情况下是模棱两可"的错误.

Unfortunately I get an error about "Type of expression is ambiguous without more context".

在这种情况下如何正确使用 ForEach ?

How to properly use ForEach for this case?

import SwiftUI
import Combine

class MyViewModel: ObservableObject {
    @Published var results: [String] = []

    init() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.results = ["Hello", "World", "!!!"]
        }
    }
}

struct MyView: View {
    @ObservedObject var model: MyViewModel

    var body: some View {
        VStack {
            List {
                ForEach($model.results) { text in
                    Text(text)
                 // ^--- Type of expression is ambiguous without more context
                }
            }
        }
    }
}

struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView(model: MyViewModel())
    }
}

P.S.如果我用 @State var结果替换模型:[String] 一切正常,但是出于我的目的,我需要单独的 class MyViewModel:ObservableObject

P.S. If I replace the model with @State var results: [String] all works fine, but I need have separate class MyViewModel: ObservableObject for my purposes

推荐答案

修复

将您的 ForEach 块更改为

ForEach(model.results, id: \.self) { text in
    Text(text)
}

说明

SwiftUI的错误消息对您没有任何帮助.真正的错误消息(如果将 Text(text)更改为 Text(text as String),并在 model.results ),是无法推断出通用参数'ID'".

SwiftUI's error messages aren't doing you any favors here. The real error message (which you will see if you change Text(text) to Text(text as String) and remove the $ before model.results), is "Generic parameter 'ID' could not be inferred".

换句话说,要使用 ForEach ,需要以两种方式之一唯一标识要迭代的元素.

In other words, to use ForEach, the elements that you are iterating over need to be uniquely identified in one of two ways.

  1. 如果元素是结构或类,则可以通过添加属性 var id:Hashable 使其符合Identifiable协议.在这种情况下,您不需要 id 参数.
  2. 另一种选择是使用 id 参数专门告诉 ForEach 用作唯一标识符的内容.更新:要确保您的集合中没有重复的元素,这取决于您自己.如果两个元素具有相同的ID,则对一个视图所做的任何更改(例如偏移)都会在两个视图上发生.
  1. If the element is a struct or class, you can make it conform to the Identifiable protocol by adding a property var id: Hashable. You don't need the id parameter in this case.
  2. The other option is to specifically tell ForEach what to use as a unique identifier using the id parameter. Update: It is up to you to guarentee that your collection does not have duplicate elements. If two elements have the same ID, any change made to one view (like an offset) will happen to both views.

在这种情况下,我们选择选项2,并告诉 ForEach 使用String元素本身作为标识符( \.self ).我们可以这样做,因为String符合Hashable协议.

In this case, we chose option 2 and told ForEach to use the String element itself as the identifier (\.self). We can do this since String conforms to the Hashable protocol.

$ 如何?

What about the $?

SwiftUI中的大多数视图仅采用应用程序的状态,并根据其状态布置外观.在此示例中,文本"视图仅获取存储在模型中的信息并显示它.但是一些视图需要能够回溯并修改应用程序的状态以响应用户:

Most views in SwiftUI only take your app's state and lay out their appearance based on it. In this example, the Text views simply take the information stored in the model and display it. But some views need to be able to reach back and modify your app's state in response to the user:

  • Toggle需要更新Bool值以响应开关
  • 滑块需要更新Double值以响应幻灯片
  • TextField需要更新字符串值以响应键入

我们通过使用 Binding< SomeType> 来确定应用程序状态与视图之间应该存在这种双向通信的方式.因此,Toggle要求您向其传递 Binding< Bool> ,滑块需要 Binding< Double> ,而TextField则需要 Binding< String> .

The way we identify that there should be this two-way communication between app state and a view is by using a Binding<SomeType>. So a Toggle requires you to pass it a Binding<Bool>, a Slider requires a Binding<Double>, and a TextField requires a Binding<String>.

这是 @State 属性包装器(或 @ObservedObject 内部的 @Published )进入的地方.该属性包装器包装它包含在 Binding 中的值(以及其他一些东西,以确保SwiftUI知道在值更改时更新视图).如果需要获取值,则可以简单地引用 myVariable ,但是如果需要绑定,则可以使用速记方式 $ myVariable .

This is where the @State property wrapper (or @Published inside of an @ObservedObject) come in. That property wrapper "wraps" the value it contains in a Binding (along with some other stuff to guarantee SwiftUI knows to update the views when the value changes). If we need to get the value, we can simply refer to myVariable, but if we need the binding, we can use the shorthand $myVariable.

因此,在这种情况下,您的原始代码包含 ForEach($ model.results).换句话说,您告诉编译器迭代此 Binding< [String]> ",但是 Binding 不是可以迭代的集合.删除 $ 时说:迭代此[String]",而数组一个可以迭代的集合.

So, in this case, your original code contained ForEach($model.results). In other words, you were telling the compiler, "Iterate over this Binding<[String]>", but Binding is not a collection you can iterate over. Removing the $ says, "Iterate over this [String]," and Array is a collection you can iterate over.

这篇关于如果数组是ObservableObject的成员,如何绑定数组和List?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆