Skip to content

Commit

Permalink
add Hierarchy-List code snippet
Browse files Browse the repository at this point in the history
  • Loading branch information
zntfdr committed Aug 15, 2021
1 parent 0d764ee commit aaaf1c1
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 0 deletions.
90 changes: 90 additions & 0 deletions Hierarchy-List/ContentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Original article here: https://www.fivestars.blog/code/swiftui-hierarchy-list.html

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 ContentView: View {
let data: [FileItem] = .spmData

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
}
}

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()
}
}
}
6 changes: 6 additions & 0 deletions Hierarchy-List/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Code snippet from [SwiftUI Hierarchy Lists][fs].

![][gif]

[fs]: https://fivestars.blog/articles/swiftui-hierarchy-list/
[gif]: spm.gif
Binary file added Hierarchy-List/spm.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit aaaf1c1

Please sign in to comment.