我使用 aForEach來顯示陣列的內容,然后通過檢查元素索引手動顯示每個元素之間的分隔符。這是我的代碼:
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代碼并且仍然得到相同的結果。然而,這僅僅是因為sanimals的陣列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)
}
}
}
}
這就是為什么 SwiftUIForEach帶有一個額外的初始化程式 ,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即使元素不符合,如何在 a 的元素之間自動添加分隔符Hashable?
uj5u.com熱心網友回復:
簡單的方法
struct ContentView: View {
let animals = ["Apple", "Bear", "Cat", "Dog", "Elephant"]
var body: some View {
VStack {
ForEach(animals, id: \.self) { animal in
Text(animal)
if animals.last != animal {
Divider()
.background(.blue)
}
}
}
}
}
通常,動物內部的型別必須是可識別的。在這種情況下,代碼將被修改為。
if animals.last.id != animal.id {...}
這將避免任何平等的要求/實作
uj5u.com熱心網友回復:
找到了解決方案!
appending(path:)似乎只適用于擦除到AnyKeyPath.- 然后,
appending(path:)回傳一個可選的AnyKeyPath?——這需要被強制轉換KeyPath<(Data.Index, Data.Element), ID>為滿足id引數。
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))
/// first create a `AnyKeyPath` that extracts the element from `enumerated`
let elementKeyPath: AnyKeyPath = \(Data.Index, Data.Element).1
/// then, append the `id` key path to `elementKeyPath` to extract the `Hashable` property
if let fullKeyPath = elementKeyPath.appending(path: id) as? KeyPath<(Data.Index, Data.Element), ID> {
ForEach(enumerated, id: fullKeyPath) { index, data in
content(data)
if let index = index as? Int, index != enumerated.count - 1 {
Divider()
.background(.blue)
}
}
}
}
}
用法:
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 {
ForEachDividerView(data: people, id: \.name) { person in
Text(person.name)
}
}
}
}
結果:

uj5u.com熱心網友回復:
一切都需要在 ForEach 中嗎?如果沒有,您可以考慮完全不使用索引:
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 {
if let first = data.first {
content(first)
ForEach(data.dropFirst(), id: id) { element in
Divider()
.background(.blue)
content(element)
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/489756.html
