如果数组是ObservableObject的成员,如何绑定数组和List? [英] How to bind an array and List if the array is a member of ObservableObject?
问题描述
我想创建一个 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.
- 如果元素是结构或类,则可以通过添加属性
var id:Hashable
使其符合Identifiable协议.在这种情况下,您不需要id
参数. - 另一种选择是使用
id
参数专门告诉ForEach
用作唯一标识符的内容.更新:要确保您的集合中没有重复的元素,这取决于您自己.如果两个元素具有相同的ID,则对一个视图所做的任何更改(例如偏移)都会在两个视图上发生.
- 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 theid
parameter in this case. - The other option is to specifically tell
ForEach
what to use as a unique identifier using theid
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屋!