SwiftUIRookie
SwiftUIRookie

Reputation: 761

SwiftUI - Use .refreshable on ForEach to empower pull to refresh experience

I'm making use of the new .refreshable() function. For Testing purposes I'm having this simple sleep function:

     func load() async {
         await Task.sleep(2 * 1_000_000_000)
     }

Appending this to a list works fine:

        .refreshable {
            await load()
        }

However: When I'm trying to use it with a ForEach Loop it's not working. Do you know how to overcome this issue? Background: I need ForEach to ensure custom styling. In the List I'm too limited. I already tried to style it using some attributes like

.buttonStyle(.plain)
.listRowSeparator(.hidden)

But I'm not able to remove the badges or the distance in blue as well as impacting the spacing between the list objects.

enter image description here

Would be great if you can let me know about your thoughts on this. I wan to avoid building up my own pull to refresh view with coordinators etc. like it was done back in SwiftUI 2.0 times :D Really love the .refreshable action.

Many Thanks!

Upvotes: 3

Views: 1413

Answers (2)

Mostafa Al Belliehy
Mostafa Al Belliehy

Reputation: 535

To fully customize the look of a List, I did the following:

  • Use ForEach to iterate within the List.
  • Add .listStyle(.plain) to List.
  • Use .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) with ForEach to get rid of any inner paddings.
  • Use .listRowSeparator(.hidden, edges: .all) with ForEach to hide separators. I found it a little bit annoying to customize it to my needs. However, there are some way in iOS 16 to do that as mentioned here.
  • Finally use overlay with ForEach as in the code block below to get rid of arrows on the right.

You can now whatever design you want within the list and get benefits of its capabilities.

List {
    ForEach(presenter.items) { item in
        ItemView(item: item)
    }
    .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
    .listRowSeparator(.hidden, edges: .all)
    .overlay {
        NavigationLink(destination: { Text("Detail View") }, label: { EmptyView() })
            .opacity(0)
    }
}
.listStyle(.plain)

Upvotes: 1

rob mayoff
rob mayoff

Reputation: 385970

If you want to use refreshable on anything besides List, it's up to you to provide the user interface for it. The refreshable documentation only says it works for List:

When you apply this modifier to a view, you set the refresh value in the view’s environment to the specified action. Controls that detect this action can change their appearance and provide a way for the user to execute a refresh.

When you apply this modifier on iOS and iPadOS to a List, the list provides a standard way for the user to refresh the content. When the user drags the top of the scrollable content area downward, the view reveals a refresh control and executes the provided action. Use an await expression inside the action to refresh your data. The refresh indicator remains visible for the duration of the awaited operation.

Upvotes: 1

Related Questions