Skip to content

Instantly share code, notes, and snippets.

@zntfdr
Last active October 5, 2022 09:12
Show Gist options
  • Save zntfdr/9425d6e18e21eeeb352df0821207871e to your computer and use it in GitHub Desktop.
Save zntfdr/9425d6e18e21eeeb352df0821207871e to your computer and use it in GitHub Desktop.

Revisions

  1. zntfdr revised this gist Jul 13, 2020. 1 changed file with 15 additions and 15 deletions.
    30 changes: 15 additions & 15 deletions Xcode-11-Hierarchy-List.swift
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@

    // Original article here: https://www.fivestars.blog/code/swiftui-hierarchy-list.html

    import SwiftUI

    @@ -27,20 +27,6 @@ struct FileItem: Identifiable {
    ]
    }

    struct FSDisclosureGroup<Label, Content>: View where Label: View, Content: View {
    @State var isExpanded: Bool = false
    var content: () -> Content
    var label: () -> Label

    @ViewBuilder
    var body: some View {
    Button(action: { self.isExpanded.toggle() }, label: { label().foregroundColor(.blue) })
    if isExpanded {
    content()
    }
    }
    }

    struct ContentView: View {
    let data: [FileItem]

    @@ -87,4 +73,18 @@ private struct RecursiveView<Data, RowContent>: View where Data: RandomAccessCol
    func containsSub(_ element: Data.Element) -> Bool {
    element[keyPath: children] != nil
    }
    }

    struct FSDisclosureGroup<Label, Content>: View where Label: View, Content: View {
    @State var isExpanded: Bool = false
    var content: () -> Content
    var label: () -> Label

    @ViewBuilder
    var body: some View {
    Button(action: { self.isExpanded.toggle() }, label: { label().foregroundColor(.blue) })
    if isExpanded {
    content()
    }
    }
    }
  2. zntfdr created this gist Jul 11, 2020.
    90 changes: 90 additions & 0 deletions Xcode-11-Hierarchy-List.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,90 @@


    import SwiftUI

    struct FileItem: Identifiable {
    let name: String
    var children: [FileItem]?

    var id: String { name }

    static let spmData: [FileItem] = [
    FileItem(name: ".gitignore"),
    FileItem(name: "Package.swift"),
    FileItem(name: "README.md"),
    FileItem(name: "Sources", children: [
    FileItem(name: "fivestars", children: [
    FileItem(name: "main.swift")
    ]),
    ]),
    FileItem(name: "Tests", children: [
    FileItem(name: "fivestarsTests", children: [
    FileItem(name: "fivestarsTests.swift"),
    FileItem(name: "XCTestManifests.swift"),
    ]),
    FileItem(name: "LinuxMain.swift")
    ])
    ]
    }

    struct FSDisclosureGroup<Label, Content>: View where Label: View, Content: View {
    @State var isExpanded: Bool = false
    var content: () -> Content
    var label: () -> Label

    @ViewBuilder
    var body: some View {
    Button(action: { self.isExpanded.toggle() }, label: { label().foregroundColor(.blue) })
    if isExpanded {
    content()
    }
    }
    }

    struct ContentView: View {
    let data: [FileItem]

    var body: some View {
    // List(data, children: \.children, rowContent: { Text($0.name) })
    HierarchyList(data: data, children: \.children, rowContent: { Text($0.name) })
    }
    }

    public struct HierarchyList<Data, RowContent>: View where Data: RandomAccessCollection, Data.Element: Identifiable, RowContent: View {
    private let recursiveView: RecursiveView<Data, RowContent>

    public init(data: Data, children: KeyPath<Data.Element, Data?>, rowContent: @escaping (Data.Element) -> RowContent) {
    self.recursiveView = RecursiveView(data: data, children: children, rowContent: rowContent)
    }

    public var body: some View {
    List {
    recursiveView
    }
    }
    }

    private struct RecursiveView<Data, RowContent>: View where Data: RandomAccessCollection, Data.Element: Identifiable, RowContent: View {
    let data: Data
    let children: KeyPath<Data.Element, Data?>
    let rowContent: (Data.Element) -> RowContent

    var body: some View {
    ForEach(data) { child in
    if self.containsSub(child) {
    FSDisclosureGroup(content: {
    RecursiveView(data: child[keyPath: self.children]!, children: self.children, rowContent: self.rowContent)
    .padding(.leading)
    }, label: {
    self.rowContent(child)
    })
    } else {
    self.rowContent(child)
    }
    }
    }

    func containsSub(_ element: Data.Element) -> Bool {
    element[keyPath: children] != nil
    }
    }