3

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

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

4

1 回答 1

9

修复

ForEach将您的块更改为

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

解释

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

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

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

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

$

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

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

我们确定应用程序状态和视图之间应该存在这种双向通信的方式是使用Binding<SomeType>. 所以 Toggle 需要你传递 a Binding<Bool>, Slider 需要 a Binding<Double>, TextField 需要 a Binding<String>

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

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

于 2019-09-23T21:42:09.140 回答