Pull down to refresh in SwiftUI

Pull down to refresh a list is something quite common for an iOS app. We got used to that gesture over the years and I find it a quick and intuitive way to perform the task.
With the increasing adoption of SwiftUI people are looking at ways to implemen…


This content originally appeared on DEV Community and was authored by Gualtiero Frigerio

Pull down to refresh a list is something quite common for an iOS app. We got used to that gesture over the years and I find it a quick and intuitive way to perform the task.
With the increasing adoption of SwiftUI people are looking at ways to implement the same mechanism, and this post is about my implementation of this very gesture.
The code is available on GitHub as usual, I updated an old repository I mentioned in my previous post about lazy loading.
I’m going to show you two ways of implementing the same thing, the first putting your content inside a particular view and the second is via a ViewModifier.

RefreshableScrollView

Let’s start with the first approach, putting your content inside a view. I called it RefreshableScrollView and you can find the implementation here.
The view has to be configured with an action, a function called when the user pulls down over a certain threshold (in my example I set 50 pixels). The component doesn’t show a progress view, so you can fully customise that part.

RefreshableScrollView(action: refreshList) {
    if isLoading {
        VStack {
            ProgressView()
            Text("loading...")
        }
    }
    LazyVStack {
        ForEach(posts) { post in
            PostView(post: post)
        }
    }
}

private func refreshList() {
    isLoading = true
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
        isLoading = false
    }
}

The example is very simple, I mimic the reloading of a set of data by calling asyncAfter to wait for a second. You’d likely have to interact with a view model to ask to fetch data again, and if the user pulls while you’re loading you may want to avoid fetching again, but you get the point.
Let’s see how RefreshableScrollView actually works

struct RefreshableScrollView: View {
    init(action: @escaping () -> Void, @ViewBuilder content: @escaping () -> Content) {
        self.content = content
        self.refreshAction = action
    }
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                content()
                    .anchorPreference(key: OffsetPreferenceKey.self, value: .top) {
                        geometry[$0].y
                    }
            }
            .onPreferenceChange(OffsetPreferenceKey.self) { offset in
                if offset > threshold {
                    refreshAction()
                }
            }
        }
    }

    // MARK: - Private

    private var content: () -> Content
    private var refreshAction: () -> Void
    private let threshold:CGFloat = 50.0
}

fileprivate struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0

    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = nextValue()
    }
}

As you can see I’m wrapping the content inside a ScrollView with a GeometryView.
The ScrollView is necessary to be able to pull down the content.
In my tests I found out that having a List inside a ScrollView doesn’t work, so use ForEach.
GeometryReader is necessary to compute the offset.
In order to get the offset, we need to use a PreferenceKey. You can see I implemented a struct OffsetPreferenceKey for that purpose, we need to provide a defaultValue and reduce, what reduce does in our example is basically keeping the value updated.
In order to use our PreferenceKey and get the offset we have to set .anchorPreference on our content. Unfortunately there isn’t documentation about this method (the notorious no overview available). You don’t need to know the details though, all you have to know is to set this preference and to implement .onPreferenceChange, where you can get the value from GeometryProxy. We asked for the .top value, and we get the y coordinate via subscript. We get a CGPoint, so we have x and y.
If the offset is bigger than the defined threshold, we can call the action to refresh the content.

RefreshableScrollViewModifier

The second approach is a ViewModifier. Internally this view modifier uses RefreshableScrollView

struct RefreshableScrollViewModifier: ViewModifier {
    var action: () -> Void

    func body(content: Content) -> some View {
        RefreshableScrollView(action: action) {
            content
        }
    }
}

and this is how to use it in your view

var body: some View {
    LazyVStack {
        if isLoading {
            ProgressView()
        }
        ForEach(posts) { post in
            PostView(post: post)
        }
    }
    .modifier(RefreshableScrollViewModifier(action: refreshAction))
}

I think I like using the RefreshableScrollView directly more than implementing the modifier, but it is up to you.

Hope you’ll have fun implementing pull down to refresh in your SwiftUI apps, happy coding ?

Original post


This content originally appeared on DEV Community and was authored by Gualtiero Frigerio


Print Share Comment Cite Upload Translate Updates
APA

Gualtiero Frigerio | Sciencx (2021-05-14T12:06:03+00:00) Pull down to refresh in SwiftUI. Retrieved from https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/

MLA
" » Pull down to refresh in SwiftUI." Gualtiero Frigerio | Sciencx - Friday May 14, 2021, https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/
HARVARD
Gualtiero Frigerio | Sciencx Friday May 14, 2021 » Pull down to refresh in SwiftUI., viewed ,<https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/>
VANCOUVER
Gualtiero Frigerio | Sciencx - » Pull down to refresh in SwiftUI. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/
CHICAGO
" » Pull down to refresh in SwiftUI." Gualtiero Frigerio | Sciencx - Accessed . https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/
IEEE
" » Pull down to refresh in SwiftUI." Gualtiero Frigerio | Sciencx [Online]. Available: https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/. [Accessed: ]
rf:citation
» Pull down to refresh in SwiftUI | Gualtiero Frigerio | Sciencx | https://www.scien.cx/2021/05/14/pull-down-to-refresh-in-swiftui/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.