我想创建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
答案 0 :(得分:4)
修复
将您的ForEach
块更改为
ForEach(model.results, id: \.self) { text in
Text(text)
}
说明
SwiftUI的错误消息对您没有任何帮助。真正的错误消息(如果将Text(text)
更改为Text(text as String)
并在$
之前删除model.results
,则会看到此错误消息)是“无法推断出通用参数'ID' “。
换句话说,要使用ForEach
,需要以两种方式之一唯一标识要迭代的元素。
var id: Hashable
使其符合Identifiable协议。在这种情况下,您不需要id
参数。ForEach
参数明确告诉id
用作唯一标识符。 更新:要确保您的集合中没有重复的元素,这取决于您自己。如果两个元素具有相同的ID,则对一个视图所做的任何更改(例如偏移)都会在两个视图上发生。在这种情况下,我们选择了选项2,并告诉ForEach
使用String元素本身作为标识符(\.self
)。我们可以这样做,因为String符合Hashable协议。
$
怎么样?
SwiftUI中的大多数视图仅采用应用程序的状态,并根据其状态布置外观。在此示例中,“文本”视图仅获取存储在模型中的信息并显示它。但是一些视图需要能够回溯并修改应用程序的状态以响应用户:
我们通过使用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]”,并且数组是一个可以迭代的集合。