我使用ForEach
来显示数组的内容,然后通过检查元素索引手动显示每个元素之间的分隔符。
struct ContentView: View {
let animals = ["Apple", "Bear", "Cat", "Dog", "Elephant"]
var body: some View {
VStack {
/// array of tuples containing each element's index and the element itself
let enumerated = Array(zip(animals.indices, animals))
ForEach(enumerated, id: \.1) { index, animal in
Text(animal)
/// add a divider if the element isn't the last
if index != enumerated.count - 1 {
Divider()
.background(.blue)
}
}
}
}
}
结果:
这是可行的,但是我希望有一种方法可以自动地在所有地方添加分隔符,而不用每次都写Array(zip(animals.indices, animals))
。
struct ForEachDividerView<Data, Content>: View where Data: RandomAccessCollection, Data.Element: Hashable, Content: View {
var data: Data
var content: (Data.Element) -> Content
var body: some View {
let enumerated = Array(zip(data.indices, data))
ForEach(enumerated, id: \.1) { index, data in
/// generate the view
content(data)
/// add a divider if the element isn't the last
if let index = index as? Int, index != enumerated.count - 1 {
Divider()
.background(.blue)
}
}
}
}
/// usage
ForEachDividerView(data: animals) { animal in
Text(animal)
}
这样做效果很好,隔离了所有的样板zip
代码,仍然得到相同的结果,但是,这只是因为animals
是String
的数组,它符合Hashable
-如果数组中的元素不符合Hashable
,它就不能工作:
struct Person {
var name: String
}
struct ContentView: View {
let people: [Person] = [
.init(name: "Anna"),
.init(name: "Bob"),
.init(name: "Chris")
]
var body: some View {
VStack {
/// Error! Generic struct 'ForEachDividerView' requires that 'Person' conform to 'Hashable'
ForEachDividerView(data: people) { person in
Text(person.name)
}
}
}
}
这就是为什么SwiftUI的ForEach
附带了一个额外的初始化器init(_:id:content:)
,它接受一个自定义的键路径来提取ID。我想在我的ForEachDividerView
中利用这个初始化器,但是我无法理解它。下面是我尝试的:
struct ForEachDividerView<Data, Content, ID>: View where Data: RandomAccessCollection, ID: Hashable, Content: View {
var data: Data
var id: KeyPath<Data.Element, ID>
var content: (Data.Element) -> Content
var body: some View {
let enumerated = Array(zip(data.indices, data))
/// Error! Invalid component of Swift key path
ForEach(enumerated, id: \.1.appending(path: id)) { index, data in
content(data)
if let index = index as? Int, index != enumerated.count - 1 {
Divider()
.background(.blue)
}
}
}
}
/// at least this part works...
ForEachDividerView(data: people, id: \.name) { person in
Text(person.name)
}
我尝试使用appending(path:)
将第一个密钥路径(从enumerated
中提取元素)与第二个密钥路径(从元素中获取Hashable
属性)组合在一起,但得到的是Invalid component of Swift key path
。
如何在ForEach
的元素之间自动添加分隔符,即使元素不符合Hashable
?
4条答案
按热度按时间nwlqm0z11#
简单方法
通常情况下,动物体内的类型必须是可识别的。在这种情况下,代码将修改为。
这将避免任何等同的要求/实现
yqlxgs2m2#
我使用the article mentioned in a comment构建了以下代码,它获取一组视图并在它们之间放置一个分隔符。
当视图不是由
ForEach
生成时,这也是有用的,特别是当一个或多个视图被有条件地移除时(例如,使用if
语句)。velaa5lx3#
是否所有内容都需要在ForEach中?如果不是,您可以考虑根本不使用索引:
8wtpewkr4#
找到解决办法了!
appending(path:)
似乎只对擦除到AnyKeyPath
的关键路径起作用。1.然后,
appending(path:)
返回一个可选的AnyKeyPath?
-这需要被转换为KeyPath<(Data.Index, Data.Element), ID>
以满足id
参数。用法:
结果: