diff --git a/README.md b/README.md index 60129da..039ffe9 100644 --- a/README.md +++ b/README.md @@ -85,8 +85,8 @@ private(set) lazy var pagination: AdvancedListPagination = { ### 📁 Move and 🗑️ delete items -You can define which actions your list should support through the `onMoveAction` and `onDeleteAction` initializer parameters. -**Per default the move and delete functions are disabled if you skip the parameters.** +To enable the move or delete function just use the related `onMove` or `onDelete` view modifier. +**Per default the functions are disabled if you don't add the view modifiers.** ```swift import AdvancedList @@ -95,11 +95,7 @@ import AdvancedList AdvancedList(yourData, content: { item in Text("Item") -}, listState: $listState, onMoveAction: { (indexSet, index) in - // do something -}, onDeleteAction: { indexSet in - // do something -}, emptyStateView: { +}, listState: $listState, emptyStateView: { Text("No data") }, errorStateView: { error in Text(error.localizedDescription) @@ -107,6 +103,12 @@ AdvancedList(yourData, content: { item in }, loadingStateView: { Text("Loading ...") }, pagination: .noPagination) +.onMove { (indexSet, index) in + // move me +} +.onDelete { indexSet in + // delete me +} ``` ### 🎛️ Filtering @@ -217,3 +219,69 @@ AdvancedList(yourData, content: { item in Text("Loading ...") }, pagination: .noPagination) ``` + +## Migration 3.0 -> 4.0 + +Thanks to a hint from @SpectralDragon I could refactor the `onMove` and `onDelete` functionality to view modifiers. + +**Before:** +```swift +import AdvancedList + +@State private var listState: ListState = .items + +AdvancedList(yourData, content: { item in + Text("Item") +}, listState: $listState, onMoveAction: { (indexSet, index) in + // move me +}, onDeleteAction: { indexSet in + // delete me +}, emptyStateView: { + Text("No data") +}, errorStateView: { error in + VStack { + Text(error.localizedDescription) + .lineLimit(nil) + + Button(action: { + // do something + }) { + Text("Retry") + } + } +}, loadingStateView: { + Text("Loading ...") +}, pagination: .noPagination) +``` + +**After:** +```swift +import AdvancedList + +@State private var listState: ListState = .items + +AdvancedList(yourData, content: { item in + Text("Item") +}, listState: $listState, emptyStateView: { + Text("No data") +}, errorStateView: { error in + VStack { + Text(error.localizedDescription) + .lineLimit(nil) + + Button(action: { + // do something + }) { + Text("Retry") + } + } +}, loadingStateView: { + Text("Loading ...") +}, pagination: .noPagination) +.onMove { (indexSet, index) in + // move me +} +.onDelete { indexSet in + // delete me +} +``` diff --git a/Sources/AdvancedList/private/Models/AnyDynamicViewContent.swift b/Sources/AdvancedList/private/Models/AnyDynamicViewContent.swift new file mode 100644 index 0000000..be61623 --- /dev/null +++ b/Sources/AdvancedList/private/Models/AnyDynamicViewContent.swift @@ -0,0 +1,22 @@ +// +// AnyDynamicViewContent.swift +// +// +// Created by Christian Elies on 20.11.19. +// + +import SwiftUI + +extension AdvancedList { + struct AnyDynamicViewContent: DynamicViewContent { + private let view: AnyView + + private(set) var data: AnyCollection + var body: some View { view } + + init(_ view: View) { + self.view = AnyView(view) + self.data = AnyCollection(view.data.map { $0 as Any }) + } + } +} diff --git a/Sources/AdvancedList/public/Views/AdvancedList.swift b/Sources/AdvancedList/public/Views/AdvancedList.swift index 9aaa93e..96a124c 100644 --- a/Sources/AdvancedList/public/Views/AdvancedList.swift +++ b/Sources/AdvancedList/public/Views/AdvancedList.swift @@ -13,27 +13,28 @@ public struct AdvancedList Void> public typealias OnDeleteAction = Optional<(IndexSet) -> Void> + private typealias Configuration = (AnyDynamicViewContent) -> AnyDynamicViewContent + @ObservedObject private var pagination: AdvancedListPagination private var data: Data private var content: (Data.Element) -> Content private var listState: Binding - private var onMoveAction: OnMoveAction = nil - private var onDeleteAction: OnDeleteAction = nil private let emptyStateView: () -> EmptyStateView private let errorStateView: (Error) -> ErrorStateView private let loadingStateView: () -> LoadingStateView @State private var isLastItem: Bool = false - public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content, listState: Binding, onMoveAction: OnMoveAction = nil, onDeleteAction: OnDeleteAction = nil, @ViewBuilder emptyStateView: @escaping () -> EmptyStateView, @ViewBuilder errorStateView: @escaping (Error) -> ErrorStateView, @ViewBuilder loadingStateView: @escaping () -> LoadingStateView, pagination: AdvancedListPagination) { + private var configurations: [Configuration] + + public init(_ data: Data, @ViewBuilder content: @escaping (Data.Element) -> Content, listState: Binding, @ViewBuilder emptyStateView: @escaping () -> EmptyStateView, @ViewBuilder errorStateView: @escaping (Error) -> ErrorStateView, @ViewBuilder loadingStateView: @escaping () -> LoadingStateView, pagination: AdvancedListPagination) { self.data = data self.content = content self.listState = listState - self.onMoveAction = onMoveAction - self.onDeleteAction = onDeleteAction self.emptyStateView = emptyStateView self.errorStateView = errorStateView self.loadingStateView = loadingStateView self.pagination = pagination + configurations = [] } } @@ -62,13 +63,31 @@ extension AdvancedList { } } +// MARK: - View modifiers +extension AdvancedList { + public func onMove(perform action: OnMoveAction) -> Self { + configure { AnyDynamicViewContent($0.onMove(perform: action)) } + } + + public func onDelete(perform action: OnDeleteAction) -> Self { + configure { AnyDynamicViewContent($0.onDelete(perform: action)) } + } +} + +// MARK: - Private helper extension AdvancedList { + private func configure(_ configuration: @escaping Configuration) -> Self { + var result = self + result.configurations.append(configuration) + return result + } + private func getListView() -> some View { List { - ForEach(data) { item in - self.getItemView(item) - }.onMove(perform: self.onMoveAction) - .onDelete(perform: self.onDeleteAction) + configurations + .reduce(AnyDynamicViewContent(ForEach(data) { item in + self.getItemView(item) + })) { (currentView, configuration) in configuration(currentView) } } } @@ -121,7 +140,7 @@ struct AdvancedList_Previews : PreviewProvider { let id: String = UUID().uuidString } - private static let items: [MockItem] = [] + private static let items: [MockItem] = Array(0...5).map { _ in MockItem() } @State private static var listState: ListState = .items static var previews: some View {