我有一个数组,并且要遍历它,以便根据数组值初始化视图,并希望根据数组项索引执行操作
当我遍历对象时
ForEach(array, id: \.self) { item in
CustomView(item: item)
.tapAction {
self.doSomething(index) // Can't get index, so this won't work
}
}
Run Code Online (Sandbox Code Playgroud)
所以,我尝试了另一种方法
ForEach((0..<array.count)) { index in
CustomView(item: array[index])
.tapAction {
self.doSomething(index)
}
}
Run Code Online (Sandbox Code Playgroud)
但随着第二种方法的问题是,当我改变阵列,例如,如果doSomething做以下
self.array = [1,2,3]
Run Code Online (Sandbox Code Playgroud)
ForEach即使值已更改,视图也不会更改。我相信,发生这种情况是因为array.count没有改变。
有解决方案吗?提前致谢。
onm*_*133 66
我通常会买enumerated一对index和element作为elementid
ForEach(Array(array.enumerated()), id: \.element) { index, element in
Text("\(index)")
Text(element.description)
}
Run Code Online (Sandbox Code Playgroud)
对于更可重用的组件,您可以访问这篇文章https://onmyway133.com/posts/how-to-use-foreach-with-indices-in-swiftui/
Sto*_*one 51
另一种方法是使用:
ForEach(Array(array.enumerated()), id: \.offset) { index, element in
// ...
}
Run Code Online (Sandbox Code Playgroud)
来源:https : //alejandromp.com/blog/swiftui-enumerated/
Sté*_*pin 18
我需要一个更通用的解决方案,它可以处理所有类型的数据(实现RandomAccessCollection),并且还可以通过使用范围来防止未定义的行为。
我最终得到了以下结果:
public struct ForEachWithIndex<Data: RandomAccessCollection, ID: Hashable, Content: View>: View {
public var data: Data
public var content: (_ index: Data.Index, _ element: Data.Element) -> Content
var id: KeyPath<Data.Element, ID>
public init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (_ index: Data.Index, _ element: Data.Element) -> Content) {
self.data = data
self.id = id
self.content = content
}
public var body: some View {
ForEach(
zip(self.data.indices, self.data).map { index, element in
IndexInfo(
index: index,
id: self.id,
element: element
)
},
id: \.elementID
) { indexInfo in
self.content(indexInfo.index, indexInfo.element)
}
}
}
extension ForEachWithIndex where ID == Data.Element.ID, Content: View, Data.Element: Identifiable {
public init(_ data: Data, @ViewBuilder content: @escaping (_ index: Data.Index, _ element: Data.Element) -> Content) {
self.init(data, id: \.id, content: content)
}
}
extension ForEachWithIndex: DynamicViewContent where Content: View {
}
private struct IndexInfo<Index, Element, ID: Hashable>: Hashable {
let index: Index
let id: KeyPath<Element, ID>
let element: Element
var elementID: ID {
self.element[keyPath: self.id]
}
static func == (_ lhs: IndexInfo, _ rhs: IndexInfo) -> Bool {
lhs.elementID == rhs.elementID
}
func hash(into hasher: inout Hasher) {
self.elementID.hash(into: &hasher)
}
}
Run Code Online (Sandbox Code Playgroud)
这样,问题中的原始代码可以替换为:
ForEachWithIndex(array, id: \.self) { index, item in
CustomView(item: item)
.tapAction {
self.doSomething(index) // Now works
}
}
Run Code Online (Sandbox Code Playgroud)
获取索引以及元素。
请注意,API被镜像到SwiftUI的-这意味着,与初始化
id参数的content闭包是不是一个@ViewBuilder。
唯一的变化是id参数是可见的并且可以更改
mal*_*hal 15
ForEachSwiftUI 与 for 循环不同,它实际上在做一些称为结构同一性的事情。状态的文档ForEach:
/// It\'s important that the `id` of a data element doesn\'t change, unless\n/// SwiftUI considers the data element to have been replaced with a new data\n/// element that has a new identity.\nRun Code Online (Sandbox Code Playgroud)\n这意味着我们不能在ForEach. 必须ForEach给出可识别项目的实际数组。这样 SwiftUI 就可以对行进行动画处理以匹配数据,显然这不能与索引一起使用,例如,如果 0 处的行移动到 1,其索引仍然是 0。
要解决获取索引的问题,您只需像这样查找索引:
\nForEach(items) { item in\n CustomView(item: item)\n .tapAction {\n if let index = array.firstIndex(where: { $0.id == item.id }) {\n self.doSomething(index) \n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n您可以在其Scrumdinger 示例应用程序教程中看到 Apple 这样做。
\nguard let scrumIndex = scrums.firstIndex(where: { $0.id == scrum.id }) else {\n fatalError("Can\'t find scrum in array")\n}\nRun Code Online (Sandbox Code Playgroud)\n
小智 11
你可以使用这个方法:
.enumerated()
来自 Swift 文档:
返回 (n, x) 对的序列,其中 n 表示从零开始的连续整数,x 表示序列的元素。
var elements: [String] = ["element 1", "element 2", "element 3", "element 4"]
ForEach(Array(elements.enumerated()), id: \.element) { index, element in
Text("\(index) \(element)")
}
Run Code Online (Sandbox Code Playgroud)
以下方法的优点是,如果状态值 \xe2\x80\x8b\xe2\x80\x8b 更改,ForEach 中的视图甚至会更改:
\n\nstruct ContentView: View {\n @State private var array = [1, 2, 3]\n\n func doSomething(index: Int) {\n self.array[index] = Int.random(in: 1..<100)\n }\n\n var body: some View { \n let arrayIndexed = array.enumerated().map({ $0 })\n\n return List(arrayIndexed, id: \\.element) { index, item in\n\n Text("\\(item)")\n .padding(20)\n .background(Color.green)\n .onTapGesture {\n self.doSomething(index: index)\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\n...这也可以用于删除列表中的最后一个分隔符\n:
\n
struct ContentView: View {\n\n init() {\n UITableView.appearance().separatorStyle = .none\n }\n\n var body: some View {\n let arrayIndexed = [Int](1...5).enumerated().map({ $0 })\n\n return List(arrayIndexed, id: \\.element) { index, number in\n\n VStack(alignment: .leading) {\n Text("\\(number)")\n\n if index < arrayIndexed.count - 1 {\n Divider()\n }\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n
对于基于非零的数组,避免使用枚举,而是使用 zip:
ForEach(Array(zip(items.indices, items)), id: \.0) { index, item in
// Add Code here
}
Run Code Online (Sandbox Code Playgroud)
这对我有用:
struct ContentView: View {
@State private var array = [1, 1, 2]
func doSomething(index: Int) {
self.array = [1, 2, 3]
}
var body: some View {
ForEach(0..<array.count) { i in
Text("\(self.array[i])")
.tapAction { self.doSomething(index: i) }
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
2021 解决方案如果您使用非基于零的数组,请避免使用枚举:
ForEach(array.indices,id:\.self) { index in
VStack {
Text(array[index].name)
.customFont(name: "STC", style: .headline)
.foregroundColor(Color.themeTitle)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我View基于令人敬畏的 Stone 的解决方案为此目的创建了一个专用的:
struct EnumeratedForEach<ItemType, ContentView: View>: View {
let data: [ItemType]
let content: (Int, ItemType) -> ContentView
init(_ data: [ItemType], @ViewBuilder content: @escaping (Int, ItemType) -> ContentView) {
self.data = data
self.content = content
}
var body: some View {
ForEach(Array(self.data.enumerated()), id: \.offset) { idx, item in
self.content(idx, item)
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在你可以像这样使用它:
EnumeratedForEach(items) { idx, item in
...
}
Run Code Online (Sandbox Code Playgroud)
要从 SwiftUI 的 ForEach 循环获取索引,您可以使用闭包的简写参数名称:
\n@State private var cars = ["Aurus","Bentley","Cadillac","Genesis"]\n\nvar body: some View {\n NavigationView {\n List {\n ForEach(Array(cars.enumerated()), id: \\.offset) {\n\n Text("\\($0.element) at \\($0.offset) index")\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n结果:
\n// Aurus at 0 index\n// Bentley at 1 index\n// Cadillac at 2 index\n// Genesis at 3 index\nRun Code Online (Sandbox Code Playgroud)\n聚苯乙烯
\n最初,我发布了一个带有所有 Swift 开发人员都习惯的“通用”表达式的答案,但是,感谢 @loremipsum,我更改了它。正如 WWDC 2021 Demystify SwiftUI视频(时间 33:40)中所述,数组索引从\\.self身份(关键路径)来看并不稳定。
ForEach(0 ..< cars.count, id: \\.self) { // \xe2\x80\x93 NOT STABLE\n Text("\\(cars[$0]) at \\($0) index")\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
2265 次 |
| 最近记录: |