Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pull down to refresh data in SwiftUI

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"))         }     } } 
like image 398
PinkeshGjr Avatar asked Jun 07 '19 11:06

PinkeshGjr


People also ask

What is pull down to refresh?

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.

How do you refresh a page in SwiftUI?

To add the pull to refresh functionality to our SwiftUI List, simply use the . refreshable modifier. List(emojiSet, id: \. self) { emoji in Text(emoji) } .

How do I use the timer in SwiftUI?

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.


2 Answers

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 ScrollViews 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 }.

like image 104
Evan Deaubl Avatar answered Sep 19 '22 21:09

Evan Deaubl


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") 
like image 45
Anthony Avatar answered Sep 21 '22 21:09

Anthony