i have used simple listing of data using List
. I would like to add pull down to refresh functionality but i am not sure which is the best possible approach.
Pull down to refresh view will only be visible when user tries to pull down from the very first index same like we did in UITableView
with UIRefreshControl
in UIKit
Here is simple code for listing data in SwiftUI
.
struct CategoryHome: View {
var categories: [String: [Landmark]] {
.init(
grouping: landmarkData,
by: { $0.category.rawValue }
)
}
var body: some View {
NavigationView {
List {
ForEach(categories.keys.sorted().identified(by: \.self)) { key in
Text(key)
}
}
.navigationBarTitle(Text("Featured"))
}
}
}
I needed the same thing for an app I'm playing around with, and it looks like the SwiftUI API does not include a refresh control capability for ScrollView
s at this time.
Over time, the API will develop and rectify these sorts of situations, but the general fallback for missing functionality in SwiftUI will always be implementing a struct that implements UIViewRepresentable
. Here's a quick and dirty one for UIScrollView
with a refresh control.
struct LegacyScrollView : UIViewRepresentable {
// any data state, if needed
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> UIScrollView {
let control = UIScrollView()
control.refreshControl = UIRefreshControl()
control.refreshControl?.addTarget(context.coordinator, action:
#selector(Coordinator.handleRefreshControl),
for: .valueChanged)
// Simply to give some content to see in the app
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
label.text = "Scroll View Content"
control.addSubview(label)
return control
}
func updateUIView(_ uiView: UIScrollView, context: Context) {
// code to update scroll view from view state, if needed
}
class Coordinator: NSObject {
var control: LegacyScrollView
init(_ control: LegacyScrollView) {
self.control = control
}
@objc func handleRefreshControl(sender: UIRefreshControl) {
// handle the refresh event
sender.endRefreshing()
}
}
}
But of course, you can't use any SwiftUI components in your scroll view without wrapping them in a UIHostingController
and dropping them in makeUIView
, rather than putting them in a LegacyScrollView() { // views here }
.
Here's an implementation that introspects the view hierarchy and adds a proper UIRefreshControl
to a SwiftUI List's table view: https://github.com/timbersoftware/SwiftUIRefresh
Bulk of the introspection logic can be found here: https://github.com/timbersoftware/SwiftUIRefresh/blob/15d9deed3fec66e2c0f6fd1fd4fe966142a891db/Sources/PullToRefresh.swift#L39-L73
Hi check out this library I made: https://github.com/AppPear/SwiftUI-PullToRefresh
You can implement it by one line of code:
struct CategoryHome: View {
var categories: [String: [Landmark]] {
.init(
grouping: landmarkData,
by: { $0.category.rawValue }
)
}
var body: some View {
RefreshableNavigationView(title: "Featured", action:{
// your refresh action
}){
ForEach(categories.keys.sorted().identified(by: \.self)) { key in
Text(key)
Divider() // !!! this is important to add cell separation
}
}
}
}
}
The swiftui-introspects has not supported on masOS yet, so if you are going to build a UI that works for both iOS and macOS, consider the Samu Andras library.
I forked his code, added a few enhancements, and added the ability to use without the NavigationView
Here is the sample code.
RefreshableList(showRefreshView: $showRefreshView, action:{
// Your refresh action
// Remember to set the showRefreshView to false
self.showRefreshView = false
}){
ForEach(self.numbers, id: \.self){ number in
VStack(alignment: .leading){
Text("\(number)")
Divider()
}
}
}
For more details, you can visit the link below.
https://github.com/phuhuynh2411/SwiftUI-PullToRefresh