Get index in ForEach in SwiftUI

前端 未结 6 1890
执笔经年
执笔经年 2020-12-24 05:01

I have an array and I want to iterate through it initialize views based on array value, and want to perform action based on array item index

When I iterate through o

6条回答
  •  温柔的废话
    2020-12-24 05:30

    I needed a more generic solution, that could work on all kind of data (that implements RandomAccessCollection), and also prevent undefined behavior by using ranges.
    I ended up with the following:

    public struct ForEachWithIndex: View {
        public var data: Data
        public var content: (_ index: Data.Index, _ element: Data.Element) -> Content
        var id: KeyPath
    
        public init(_ data: Data, id: KeyPath, 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: Hashable {
        let index: Index
        let id: KeyPath
        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)
        }
    }
    

    This way, the original code in the question can just be replaced by:

    ForEachWithIndex(array, id: \.self) { index, item in
      CustomView(item: item)
        .tapAction {
          self.doSomething(index) // Now works
        }
    }
    

    To get the index as well as the element.

    Note that the API is mirrored to that of SwiftUI - this means that the initializer with the id parameter's content closure is not a @ViewBuilder.
    The only change from that is the id parameter is visible and can be changed

提交回复
热议问题