This package provides a wrapper view around the SwiftUIList view which adds pagination (through my ListPagination package) and an empty, error and loading state including a corresponding view.
📦 Installation
Add this Swift package in Xcode using its Github repository url. (File > Swift Packages > Add Package Dependency…)
🚀 How to use
The AdvancedList view is similar to the List and ForEach views. You have to pass data (RandomAccessCollection) and a view provider ((Data.Element) -> some View) to the initializer. In addition to the List view the AdvancedList expects a list state and corresponding views.
Modify your data anytime or hide an item through the content block if you like. The view is updated automatically 🎉.
Starting from version 6.0.0 you can use a custom list view instead of the SwiftUIList used under the hood. As an example you can now easily use the LazyVStack introduced in iOS 14 if needed.
Upgrade from version 5.0.0without breaking anything. Simply add the listView parameter after the upgrade:
Starting from version 8.0.0 you have full freedom & control over the content view rendered in the items state of your AdvancedList. Use a SwiftUI List or a custom view.
Upgrade from version 7.0.0without breaking anything and use the new API:
The Pagination functionality is now (>= 5.0.0) implemented as a modifier.
It has three different states: error, idle and loading. If the state of the Pagination changes the AdvancedList displays the view created by the view builder of the specified pagination object (AdvancedListPagination). Keep track of the current pagination state by creating a local state variable (@State) of type AdvancedListPaginationState. Use this state variable in the contentViewBuilder of your pagination configuration object to determine which view should be displayed in the list (see the example below).
If you want to use pagination you can choose between the lastItemPagination and the thresholdItemPagination. Both concepts are described here. Just specify the type of the pagination when adding the .pagination modifier to your AdvancedList.
The view created by the contentViewBuilder of your pagination configuration object will only be visible below the List if the last item of the List appeared! That way the user is only interrupted if needed.
Example:
@State private var paginationState: AdvancedListPaginationState = .idle
AdvancedList(...)
.pagination(.init(type: .lastItem, shouldLoadNextPage: {
paginationState = .loading
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
items.append(contentsOf: moreItems)
paginationState = .idle
}
}) {
switch paginationState {
case .idle:
EmptyView()
case .loading:
if #available(iOS 14, *) {
ProgressView()
} else {
Text("Loading ...")
}
case let .error(error):
Text(error.localizedDescription)
}
})
📁 Move and 🗑️ delete items
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.
import AdvancedList
@State private var listState: ListState = .items
AdvancedList(yourData, content: { item in
Text("Item")
}, listState: listState, emptyStateView: {
Text("No data")
}, errorStateView: { error in
Text(error.localizedDescription)
.lineLimit(nil)
}, loadingStateView: {
Text("Loading ...")
})
.onMove { (indexSet, index) in
// move me
}
.onDelete { indexSet in
// delete me
}
🎛️ Filtering
You can hide items in your list through the content block. Only return a view in the content block if a specific condition is met.
🎁 Example
The following code shows how easy-to-use the view is:
For more examples take a look at the Example directory.
Migration
Migration 2.x -> 3.0
The AdvancedList was dramatically simplified and is now more like the List and ForEach SwiftUI views.
Delete your list service instances and directly pass your data to the list initializer
Create your views through a content block (initializer parameter) instead of conforming your items to View directly (removed type erased wrapper AnyListItem)
Pass a list state binding to the initializer (before: the ListService managed the list state)
Move and delete: Instead of setting AdvancedListActions on your list service just pass a onMoveAction and/or onDeleteAction block to the initializer
Before:
import AdvancedList
let listService = ListService()
listService.supportedListActions = .moveAndDelete(onMove: { (indexSet, index) in
// please move me
}, onDelete: { indexSet in
// please delete me
})
listService.listState = .loading
AdvancedList(listService: listService, emptyStateView: {
Text("No data")
}, errorStateView: { error in
VStack {
Text(error.localizedDescription)
.lineLimit(nil)
Button(action: {
// do something
}) {
Text("Retry")
}
}
}, loadingStateView: {
Text("Loading ...")
}, pagination: .noPagination)
listService.listState = .loading
// fetch your items ...
listService.appendItems(yourItems)
listService.listState = .items
After:
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)
Migration 3.0 -> 4.0
Thanks to a hint from @SpectralDragon I could refactor the onMove and onDelete functionality to view modifiers.
Before:
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:
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
}
Migration 4.0 -> 5.0
Pagination is now implemented as a modifier 💪 And last but not least the code documentation arrived 😀
AdvancedList
This package provides a wrapper view around the SwiftUI
List view
which adds pagination (through my ListPagination package) and an empty, error and loading state including a corresponding view.📦 Installation
Add this Swift package in Xcode using its Github repository url. (File > Swift Packages > Add Package Dependency…)
🚀 How to use
The
AdvancedList
view is similar to theList
andForEach
views. You have to pass data (RandomAccessCollection
) and a view provider ((Data.Element) -> some View
) to the initializer. In addition to theList
view theAdvancedList
expects a list state and corresponding views. Modify your data anytime or hide an item through the content block if you like. The view is updated automatically 🎉.🆕 Custom List view
Starting from version
6.0.0
you can use a custom list view instead of theSwiftUI
List
used under the hood. As an example you can now easily use the LazyVStack introduced in iOS 14 if needed.Upgrade from version
5.0.0
without breaking anything. Simply add the listView parameter after the upgrade:🆕 Custom Content view
Starting from version
8.0.0
you have full freedom & control over the content view rendered in theitems
state of yourAdvancedList
. Use aSwiftUI List
or acustom view
.Upgrade from version
7.0.0
without breaking anything and use the new API:📄 Pagination
The
Pagination
functionality is now (>=5.0.0
) implemented as amodifier
. It has three different states:error
,idle
andloading
. If thestate
of thePagination
changes theAdvancedList
displays the view created by the view builder of the specified pagination object (AdvancedListPagination
). Keep track of the current pagination state by creating a local state variable (@State
) of typeAdvancedListPaginationState
. Use this state variable in thecontent
ViewBuilder
of your pagination configuration object to determine which view should be displayed in the list (see the example below).If you want to use pagination you can choose between the
lastItemPagination
and thethresholdItemPagination
. Both concepts are described here. Just specify the type of the pagination when adding the.pagination
modifier to yourAdvancedList
.The view created by the
content
ViewBuilder
of your pagination configuration object will only be visible below the List if the last item of the List appeared! That way the user is only interrupted if needed.Example:
📁 Move and 🗑️ delete items
To enable the move or delete function just use the related
onMove
oronDelete
view modifier. Per default the functions are disabled if you don’t add the view modifiers.🎛️ Filtering
You can hide items in your list through the content block. Only return a view in the content block if a specific condition is met.
🎁 Example
The following code shows how easy-to-use the view is:
For more examples take a look at the
Example
directory.Migration
Migration 2.x -> 3.0
The
AdvancedList
was dramatically simplified and is now more like theList
andForEach
SwiftUI views.View
directly (removed type erased wrapperAnyListItem
)ListService
managed the list state)AdvancedListActions
on your list service just pass aonMoveAction
and/oronDeleteAction
block to the initializerBefore:
After:
Migration 3.0 -> 4.0
Thanks to a hint from @SpectralDragon I could refactor the
onMove
andonDelete
functionality to view modifiers.Before:
After:
Migration 4.0 -> 5.0
Pagination
is now implemented as amodifier
💪 And last but not least the code documentation arrived 😀Before:
After:
Migration 6.0 -> 7.0
I replaced the unnecessary listState
Binding
and replaced it with a simple value parameter.Before:
After:
Migration 7.0 -> 8.0
Nothing to do 🎉