Skip to content

Commit 9ffa471

Browse files
author
Chris
authored
Merge pull request #13 from crelies/dev
Custom List View
2 parents 489289c + f3ed518 commit 9ffa471

File tree

8 files changed

+89
-24
lines changed

8 files changed

+89
-24
lines changed

README.md

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,35 @@ AdvancedList(yourData, content: { item in
3232
.lineLimit(nil)
3333
}, loadingStateView: {
3434
Text("Loading ...")
35-
}, pagination: .noPagination)
35+
})
36+
```
37+
38+
### 🆕 Custom List view
39+
40+
Starting from version `6.0.0` you can use a custom list view instead of the `SwiftUI` `List` used under the hood. As an example you can now easily use the **LazyVStack** introduced in **iOS 14** if needed.
41+
42+
Upgrade from version `5.0.0` **without breaking anything**. Simply add the **listView parameter** after the upgrade:
43+
44+
```swift
45+
AdvancedList(yourData, listView: { rows in
46+
if #available(iOS 14, macOS 11, *) {
47+
ScrollView {
48+
LazyVStack(alignment: .leading, content: rows)
49+
.padding()
50+
}
51+
} else {
52+
List(content: rows)
53+
}
54+
}, content: { item in
55+
Text("Item")
56+
}, listState: $listState, emptyStateView: {
57+
Text("No data")
58+
}, errorStateView: { error in
59+
Text(error.localizedDescription)
60+
.lineLimit(nil)
61+
}, loadingStateView: {
62+
Text("Loading ...")
63+
})
3664
```
3765

3866
### 📄 Pagination
@@ -91,7 +119,7 @@ AdvancedList(yourData, content: { item in
91119
.lineLimit(nil)
92120
}, loadingStateView: {
93121
Text("Loading ...")
94-
}, pagination: .noPagination)
122+
})
95123
.onMove { (indexSet, index) in
96124
// move me
97125
}
@@ -130,7 +158,7 @@ AdvancedList(yourData, content: { item in
130158
}
131159
}, loadingStateView: {
132160
Text("Loading ...")
133-
}, pagination: .noPagination)
161+
})
134162
```
135163

136164
For more examples take a look at [AdvancedList-SwiftUI](https://github.com/crelies/AdvancedList-SwiftUI).

Sources/AdvancedList/private/Models/AnyAdvancedListPagination.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// AnyAdvancedListPagination.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 31.07.20.
66
//

Sources/AdvancedList/private/Models/ListState+error.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// ListState+error.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 02.08.20.
66
//

Sources/AdvancedList/public/Models/AdvancedListPagination.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// AdvancedListPagination.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 15.08.19.
66
//

Sources/AdvancedList/public/Models/AdvancedListPaginationState.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// AdvancedListPaginationState.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 17.08.19.
66
//

Sources/AdvancedList/public/Models/AdvancedListPaginationType.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// AdvancedListPaginationType.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 16.08.19.
66
//

Sources/AdvancedList/private/Models/AnyDynamicViewContent.swift renamed to Sources/AdvancedList/public/Models/AnyDynamicViewContent.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
//
22
// AnyDynamicViewContent.swift
3-
//
3+
// AdvancedList
44
//
55
// Created by Christian Elies on 20.11.19.
66
//
77

88
import SwiftUI
99

10-
struct AnyDynamicViewContent: DynamicViewContent {
10+
/// Type erased dynamic view content that generates views from an underlying collection of data.
11+
public struct AnyDynamicViewContent: DynamicViewContent {
1112
private let view: AnyView
1213

13-
private(set) var data: AnyCollection<Any>
14-
var body: some View { view }
14+
public private(set) var data: AnyCollection<Any>
15+
16+
public var body: some View { view }
1517

1618
init<View: DynamicViewContent>(_ view: View) {
1719
self.view = AnyView(view)

Sources/AdvancedList/public/Views/AdvancedList.swift

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ import SwiftUI
1212
/// An `advanced` container that presents rows of data arranged in a single column.
1313
/// Built-in `empty`, `error` and `loading` state.
1414
/// Supports `lastItem` or `thresholdItem` pagination.
15-
public struct AdvancedList<Data: RandomAccessCollection, Content: View, EmptyStateView: View, ErrorStateView: View, LoadingStateView: View> : View where Data.Element: Identifiable {
15+
public struct AdvancedList<Data: RandomAccessCollection, ListView: View, Content: View, EmptyStateView: View, ErrorStateView: View, LoadingStateView: View> : View where Data.Element: Identifiable {
16+
public typealias Rows = () -> AnyDynamicViewContent
1617
public typealias OnMoveAction = Optional<(IndexSet, Int) -> Void>
1718
public typealias OnDeleteAction = Optional<(IndexSet) -> Void>
1819

1920
private typealias Configuration = (AnyDynamicViewContent) -> AnyDynamicViewContent
2021

2122
private var pagination: AnyAdvancedListPagination?
2223
private var data: Data
24+
private var listView: ((Rows) -> ListView)?
2325
private var content: (Data.Element) -> Content
2426
private var listState: Binding<ListState>
2527
private let emptyStateView: () -> EmptyStateView
@@ -33,6 +35,30 @@ public struct AdvancedList<Data: RandomAccessCollection, Content: View, EmptySta
3335
///
3436
/// - Parameters:
3537
/// - data: The data for populating the list.
38+
/// - listView: A view builder that creates a custom list view from the given type erased dynamic view content representing the rows of the list.
39+
/// - content: A view builder that creates the view for a single row of the list.
40+
/// - listState: A binding to a property that determines the state of the list.
41+
/// - emptyStateView: A view builder that creates the view for the empty state of the list.
42+
/// - errorStateView: A view builder that creates the view for the error state of the list.
43+
/// - loadingStateView: A view builder that creates the view for the loading state of the list.
44+
public init(_ data: Data, @ViewBuilder listView: @escaping (Rows) -> ListView, @ViewBuilder content: @escaping (Data.Element) -> Content, listState: Binding<ListState>, @ViewBuilder emptyStateView: @escaping () -> EmptyStateView, @ViewBuilder errorStateView: @escaping (Error) -> ErrorStateView, @ViewBuilder loadingStateView: @escaping () -> LoadingStateView) {
45+
self.data = data
46+
self.listView = listView
47+
self.content = content
48+
self.listState = listState
49+
self.emptyStateView = emptyStateView
50+
self.errorStateView = errorStateView
51+
self.loadingStateView = loadingStateView
52+
configurations = []
53+
}
54+
}
55+
56+
extension AdvancedList where ListView == List<Never, AnyDynamicViewContent> {
57+
/// Initializes the list with the given values.
58+
/// Uses the native `SwiftUI` `List` as list view.
59+
///
60+
/// - Parameters:
61+
/// - data: The data for populating the list.
3662
/// - content: A view builder that creates the view for a single row of the list.
3763
/// - listState: A binding to a property that determines the state of the list.
3864
/// - emptyStateView: A view builder that creates the view for the empty state of the list.
@@ -46,6 +72,7 @@ public struct AdvancedList<Data: RandomAccessCollection, Content: View, EmptySta
4672
self.errorStateView = errorStateView
4773
self.loadingStateView = loadingStateView
4874
configurations = []
75+
listView = getListView
4976
}
5077
}
5178

@@ -55,7 +82,9 @@ extension AdvancedList {
5582
case .items:
5683
if !data.isEmpty {
5784
VStack {
58-
getListView()
85+
if let listView = listView {
86+
listView(rows)
87+
}
5988

6089
if let pagination = pagination, isLastItem {
6190
pagination.content()
@@ -104,23 +133,29 @@ extension AdvancedList {
104133
}
105134

106135
// MARK: - Private helper
107-
extension AdvancedList {
136+
private extension AdvancedList {
108137
private func configure(_ configuration: @escaping Configuration) -> Self {
109138
var result = self
110139
result.configurations.append(configuration)
111140
return result
112141
}
113142

114-
private func getListView() -> some View {
115-
List {
116-
configurations
117-
.reduce(AnyDynamicViewContent(ForEach(data) { item in
118-
getItemView(item)
119-
})) { (currentView, configuration) in configuration(currentView) }
120-
}
143+
func getListView(rows: Rows) -> List<Never, AnyDynamicViewContent> {
144+
List(content: rows)
145+
}
146+
147+
func rows() -> AnyDynamicViewContent {
148+
configurations
149+
.reduce(
150+
AnyDynamicViewContent(
151+
ForEach(data) { item in
152+
getItemView(item)
153+
}
154+
)
155+
) { (currentView, configuration) in configuration(currentView) }
121156
}
122157

123-
private func getItemView(_ item: Data.Element) -> some View {
158+
func getItemView(_ item: Data.Element) -> some View {
124159
content(item)
125160
.onAppear {
126161
listItemAppears(item)
@@ -131,7 +166,7 @@ extension AdvancedList {
131166
}
132167
}
133168

134-
private func listItemAppears(_ item: Data.Element) {
169+
func listItemAppears(_ item: Data.Element) {
135170
guard let pagination = pagination else {
136171
return
137172
}

0 commit comments

Comments
 (0)