如果数组是 ObservableObject 的成员,如何绑定数组和 List?

Posted

技术标签:

【中文标题】如果数组是 ObservableObject 的成员,如何绑定数组和 List?【英文标题】:How to bind an array and List if the array is a member of ObservableObject? 【发布时间】:2019-09-23 20:44:49 【问题描述】:

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

很遗憾,我收到一条关于“没有更多上下文的表达式类型不明确”的错误。

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

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())
    

附:如果我用@State var results: [String] 替换模型一切正常,但我需要单独的class MyViewModel: ObservableObject 用于我的目的

【问题讨论】:

【参考方案1】:

修复

将您的 ForEach 块更改为

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

说明

SwiftUI 的错误消息在这里对你没有任何帮助。真正的错误消息(如果您将Text(text) 更改为Text(text as String) 并在model.results 之前删除$,您将看到)是“无法推断通用参数'ID'”。

换句话说,要使用ForEach,您正在迭代的元素需要以两种方式之一进行唯一标识。

    如果元素是结构或类,您可以通过添加属性var id: Hashable 使其符合可识别协议。在这种情况下,您不需要 id 参数。 另一个选项是使用id 参数专门告诉ForEach 使用什么作为唯一标识符。 更新:由您来保证您的收藏没有重复的元素。如果两个元素具有相同的 ID,则对一个视图所做的任何更改(如偏移)都会发生在两个视图上。

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

$ 呢?

SwiftUI 中的大多数视图只获取应用的状态并根据它来布置外观。在此示例中,文本视图只是获取存储在模型中的信息并显示它。但是有些视图需要能够返回并修改应用的状态以响应用户:

Toggle 需要更新 Bool 值以响应开关 Slider 需要更新 Double 值以响应幻灯片 TextField 需要更新字符串值以响应输入

我们确定应用状态和视图之间应该存在这种双向通信的方法是使用Binding<SomeType>。因此,Toggle 需要您传递 Binding<Bool>,Slider 需要 Binding<Double>,TextField 需要 Binding<String>

这就是@State 属性包装器(或@ObservedObject 内部的@Published)的用武之地。该属性包装器“包装”它包含在Binding 中的值(以及其他一些要保证的东西) SwiftUI 知道在值更改时更新视图)。如果需要获取值,可以简单的参考myVariable,但是如果需要绑定,可以使用简写$myVariable

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

【讨论】:

好的,那么当结果在没有 $-prefix 的情况下发生变化时,这个解决方案会更新 View 吗? @DenisKreshikhin 我更新了帖子以澄清为什么你不需要(实际上不能拥有)那里的 $。

以上是关于如果数组是 ObservableObject 的成员,如何绑定数组和 List?的主要内容,如果未能解决你的问题,请参考以下文章

切换选项卡后 UI 会随 ObservableObject 发生变化

CUDA/thrust 中分段数据的成对操作

计算 numpy 数组和 csr_matrix 之间的成对最小值的最有效方法

用自定义方法,传入成绩数组,实现输出考试成绩的成三名

在 SwiftUI 中将 ObservableObject 链接到 DatePicker

python中最快的成对距离度量