swiftlistuirefreshcontrolswiftui

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"))
        }
    }
}

Solution

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