PinkeshGjr
PinkeshGjr

Reputation: 8680

Load more functionality using SwiftUI

i have used ScrollView with HStack, now i need to load more data when user reached scrolling at last.

var items: [Landmark]

i have used array of items which i am appeding in HStack using ForEach

ScrollView(showsHorizontalIndicator: false) {
    HStack(alignment: .top, spacing: 0) {
        ForEach(self.items) { landmark in
            CategoryItem(landmark: landmark)
        }
    }
}

What is the best possible solution to manage load more in SwiftUI without using custom action like loadmore button.

Upvotes: 11

Views: 10747

Answers (2)

Brogrammer
Brogrammer

Reputation: 482

If you want to keep using List with Data instead of Range, you could implement the next script:

struct ContentView: View {

    @State var items: [Landmark]

    var body: some View {
        List {
            ForEach(self.items) { landmark in
                CategoryItem(landmark: landmark)
                   .onAppear {
                      checkForMore(landmark)
                   }
            }
        }
    }

    func checkForMore(_ item: LandMark) {
       guard let item = item else { return }

       let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
        if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
           // function to request more data
            getMoreLandMarks()
        }
    }
}

You should work in a ViewModel and separate the logic from the UI.

Credits to Donny Wals: Complete example

Upvotes: 2

DenFav
DenFav

Reputation: 2811

It's better to use ForEach and List for this purpose

struct ContentView : View {
    @State var textfieldText: String = "String "
    private let chunkSize = 10
    @State var range: Range<Int> = 0..<1

    var body: some View {
        List {
            ForEach(range) { number in
                Text("\(self.textfieldText) \(number)")
            }
            Button(action: loadMore) {
                Text("Load more")
            }
        }
    }

    func loadMore() {
        print("Load more...")
        self.range = 0..<self.range.upperBound + self.chunkSize
    }
}

In this example each time you press load more it increases range of State property. The same you can do for BindableObject. If you want to do it automatically probably you should read about PullDownButton(I'm not sure if it works for PullUp)

UPD: As an option you can download new items by using onAppear modifier on the last cell(it is a static button in this example)

var body: some View {
        List {
            ForEach(range) { number in
                Text("\(self.textfieldText) \(number)")
            }
            Button(action: loadMore) {
                Text("")
                }
                .onAppear {
                    DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
                        self.loadMore()
                    }
            }
        }
    }

Keep in mind, that dispatch is necessary, because without it you will have an error saying "Updating table view while table view is updating). Possible you may using another async way to update the data

Upvotes: 11

Related Questions