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

时间:2019-09-23 20:44:49

标签: swift swiftui combine swiftui-list

我想创建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())
    }
}

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

1 个答案:

答案 0 :(得分:4)

修复

将您的ForEach块更改为

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

说明

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

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

  1. 如果元素是结构或类,则可以通过添加属性var id: Hashable使其符合Identifiable协议。在这种情况下,您不需要id参数。
  2. 另一种选择是使用ForEach参数明确告诉id用作唯一标识符。 更新:要确保您的集合中没有重复的元素,这取决于您自己。如果两个元素具有相同的ID,则对一个视图所做的任何更改(例如偏移)都会在两个视图上发生。

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

$怎么样?

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

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

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

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

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