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")) } } }
Pull-to-refresh lets a user pull down on a list of data using touch in order to retrieve more data. Pull-to-refresh is widely used on devices with a touch screen.
To add the pull to refresh functionality to our SwiftUI List, simply use the . refreshable modifier. List(emojiSet, id: \. self) { emoji in Text(emoji) } .
If you want to run some code regularly, perhaps to make a countdown timer or similar, you should use Timer and the onReceive() modifier. It's important to use . main for the runloop option, because our timer will update the user interface.
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 is a simple, small and pure SwiftUI solution i made in order to add pull to refresh functionality to a ScrollView.
struct PullToRefresh: View { var coordinateSpaceName: String var onRefresh: ()->Void @State var needRefresh: Bool = false var body: some View { GeometryReader { geo in if (geo.frame(in: .named(coordinateSpaceName)).midY > 50) { Spacer() .onAppear { needRefresh = true } } else if (geo.frame(in: .named(coordinateSpaceName)).maxY < 10) { Spacer() .onAppear { if needRefresh { needRefresh = false onRefresh() } } } HStack { Spacer() if needRefresh { ProgressView() } else { Text("⬇️") } Spacer() } }.padding(.top, -50) } }
To use it it's simple, just add it at the top of your ScrollView and give it the coordinate space of the ScrollView :
ScrollView { PullToRefresh(coordinateSpaceName: "pullToRefresh") { // do your stuff when pulled } Text("Some view...") }.coordinateSpace(name: "pullToRefresh")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With