SwiftUI 里的ForEach这么用你知道吗?
背景
最近开发的时候使用ForEach
遇见了一个错误:
ForEach<Range<Int>, Int, ZStack<TupleView<(NavigationLink<EmptyView, AnyView>,
VStack<TupleView<(ModifiedContent<XXXView,
AddGestureModifier<_EndedGesture<TapGesture>>>, Optional<XXXView>)>>)>>>
count (16) != its initial count (3).
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable`
or use `ForEach(_:id:content:)` and provide an explicit `id`!
进入正题,对与熟悉一些SwiftUI的同学来说,ForEach
很常见,也就是当你使用List
的时候,通常会用他来循环。
举个例子:
struct AView: View {
let foo = ["a", "b", "c"]
var body: some View {
List {
ForEach(foo, id:\.self) { text in
Text(text)
}
}
}
}
效果是这样的:
一切都很美好,像极了爱情啊。。。
可是呢,我需要使用到index
,这也没啥,也就式是说我要这样式的写不就可以了。
var body: some View {
List {
ForEach(foo.indices) { index in
Text(foo[index])
}
}
}
nice! 就是这么美好。
我还用到了indices, 而不是0..<foo.count
帅不帅,我就是编程界最靓的仔。
下面我要加个按钮,然后点击的时候,就更改数组。这也难不住我:
struct AView: View {
@State var foo = ["a", "b", "c"]
var body: some View {
ZStack {
Color.white
List {
ForEach(foo.indices) { index in
Text(foo[index])
}
}
Button("Add") {
foo.append("foo")
}
}
}
}
然。。。这又像极了爱情,她甩脾气。
开头的错误就出现了。。。
ForEach<Range<Int>, Int, Text> count (4) != its initial count (3). `ForEach(_:content:)` should only be used for *constant* data. Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!
有错误咱们就改,看看错误人家都表达的很直接了一点不隐讳:
解决方案: `Identifiable` or use `ForEach(_:id:content:)`
先试试用id
,
ForEach(foo.indices, id:\.self) { index in
Text(foo[index])
}
到这里呢,问题就解决了。
下面再看看另外一个 Identifiable
,
/// A class of types whose instances hold the value of an entity with stable
/// identity.
///
/// Use the `Identifiable` protocol to provide a stable notion of identity to a
/// class or value type. For example, you could define a `User` type with an `id`
/// property that is stable across your app and your app's database storage.
/// You could use the `id` property to identify a particular user even if other
/// data fields change, such as the user's name.
也很直接,就是唯一标识ID。
然后呢还有:
/// Identities could be any of the following:
///
/// - Guaranteed always unique (e.g. UUIDs).
/// - Persistently unique per environment (e.g. database record keys).
/// - Unique for the lifetime of a process (e.g. global incrementing integers).
/// - Unique for the lifetime of an object (e.g. object identifiers).
/// - Unique within the current collection (e.g. collection index).
///
这个protocol里面有个ID,可以是数据库的记录键值,全局的整形,UUID等吧,那我们现在就明白了。
既然人家需要咱们就提供,于是乎你就可以用extension
来实现:
extension String: Identifiable {
public var id: String { self }
}
注意:这里呢,如果用forEach(foo)
也就是不用indices
是可以工作的,就不用id:\.self
. 对于forEach(foo.indices)
, 是不可以的。
试过让Int
conform Identifiable
,不工作,发现foo.indices
是Range<Int>
最终这样就工作了:
forEach(Array(foo.indices))
爱情回到了最初的美好。
有时候可能因为一种天气,或者是一种空气的味道勾起你美好的回忆,想到某人,这就是生活美好的地方吧。
编辑于 2020-11-11 11:24