1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
import SwiftUI
/// A plain simple list.
/// * separators reaching all the way
/// * the height of all items in the list are based on the highest element
/// * the list is not higher than all its items
/// * transparent background
struct MullvadList<Content: View, Data: RandomAccessCollection<ID>, ID: Hashable>: View {
let data: Data
let id: KeyPath<Data.Element, ID>
let content: (Data.Element) -> Content
let header: (() -> AnyView)?
let footer: (() -> AnyView)?
init(
_ data: Data,
id: KeyPath<Data.Element, ID>,
header: (() -> some View)? = nil,
footer: (() -> some View)? = nil,
@ViewBuilder content: @escaping (Data.Element) -> Content
) {
self.data = data
self.id = id
self.header = header.map { builder in { AnyView(builder()) } }
self.footer = footer.map { builder in { AnyView(builder()) } }
self.content = content
}
init(
_ data: Data,
header: (() -> some View)? = nil,
footer: (() -> some View)? = nil,
@ViewBuilder content: @escaping (Data.Element) -> Content
) where Data.Element == ID {
self.init(data, id: \.self, header: header, footer: footer, content: content)
}
var body: some View {
VStack(alignment: .leading) {
List {
if let headerView = header?() {
headerView
.listRowStyling(insets: EdgeInsets(UIMetrics.contentHeadingLayoutMargins))
}
let lastItem = data.last
ForEach(data, id: id) { item in
VStack(spacing: 0) {
content(item)
if item != lastItem {
RowSeparator()
}
}
.listRowStyling()
}
if let footerView = footer?() {
footerView
.listRowStyling(insets: EdgeInsets(UIMetrics.contentFooterLayoutMargins))
}
}
.listStyle(.plain)
.listRowSpacing(.zero)
.environment(\.defaultMinListRowHeight, 0)
}
}
}
extension View {
func listRowStyling(
background: Color = .clear,
separator: Visibility = .hidden,
insets: EdgeInsets = .init()
) -> some View {
apply {
$0
.listRowBackground(background)
.listRowSeparator(separator)
.listRowInsets(insets)
}
}
}
#Preview {
MullvadList(
[1, 2, 3],
header: {
Text("Header")
},
footer: {
Text("Footer")
},
content: { item in
Text("Item \(item)").padding()
}
)
}
|