Reputation: 147
I'm fairly new to SwiftUI and I'm trying to figure out a problem that I can't seem to solve. I am building an app based on the GitHub API. In one of the screens, I am showing all the followers that a particular user has.
What I am trying to accomplish is that I'm trying to know when the user has scrolled to the bottom of the list of followers as I currently only grab 100 records from the server.
How can I know when the user has scrolled to the bottom of those 100 followers so that I can initiate another request for 100 more records in the 2nd and subsequent pages?
I have googled this and saw things about the ScrollViewReader, GeometryReader but I am not sure how to fix this. Here is the code in this particular screen:
import SwiftUI
struct FollowersScreen: View {
@EnvironmentObject var savedFavorites: SavedFavorites
@State var userID: String
@State private var isPresented = false
@State private var followers: [Follower] = []
@State private var page = 1
@State private var user: User? = nil
let columns = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
VStack {
ScrollView {
LazyVGrid(columns: columns, content: {
ForEach(followers, id: \.self) { follower in
FollowerCell(follower: follower)
.onAppear(perform: {
// What do I do here to get 100 more records properly?
})
.onTapGesture {
self.getFollowerDetails(userID: follower.login)
}// ON TAP GESTURE
}// FOR EACH
})// LAZYVGRID
.padding(.horizontal)
}// SCROLL VIEW
}// VSTACK
.onAppear(perform: {
self.getFollowers(userID: userID, page: 1)
})
.navigationTitle("\(userID) Followers")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing:
Button(action: {
addFavourite(gitHubUser: userID)
}) {
Image(systemName: Images.heartCircle)
.resizable()
.scaledToFit()
.frame(width: 33, height: 33, alignment: .center)
}
)// NAV BAR ITEMS
.sheet(isPresented: $isPresented, onDismiss: nil) {
FollowersDetailScreen(gitHubUser: $userID, isPresented: $isPresented, followers: $followers, user: user)
}
.environmentObject(savedFavorites)
}// BODY
private func getFollowers(userID: String, page: Int) {
NetworkManager.shared.getFollowers(for: userID, page: page) { result in
switch result {
case .success(let followers):
self.followers += followers
print("**** Count of Followers: \(self.followers.count)")
case .failure(let error):
print("getFollowers(gitHubUser:, page:) -> Error: \(error)")
}
}
}
private func getFollowerDetails(userID: String) {
NetworkManager.shared.getUserInfo(for: userID) { result in
switch result {
case .success(let user):
print("Success, found the user info!")
self.user = user
guard let tempUser = self.user else { return }
print(tempUser)
self.isPresented = true
case .failure(let error):
print("getUser(gitHubUser:, page:) -> Error: \(error)")
}
}
}
private func addFavourite(gitHubUser: String) {
NetworkManager.shared.getUserInfo(for: gitHubUser) { result in
switch result {
case .success(let user):
let favorite = Follower(login: user.login, avatarUrl: user.avatarUrl)
savedFavorites.favorites.append(favorite)
PersistenceManager.shared.encodeJSONToDisk(favorites: savedFavorites.favorites)
case .failure(let error):
print("getFavourite(gitHubUser:, page:) -> Error: \(error)")
}
}
}
}
struct FollowersScreen_Previews: PreviewProvider {
static var previews: some View {
FollowersScreen(userID: "sallen0400")
}
}
Upvotes: 0
Views: 1113
Reputation: 9755
You don't need to know when you are at the bottom of the LazyVGrid
, you need to know when you are running out of data. You need the index of where you are in the list, and compare it to the count of your array. Also, I had to use some pseudocode as I didn't have all of your types:
LazyVGrid(columns: columns, content: {
// Zip gives you both the index and the item. You could just get the index, but the
// code reads better this way, IMHO
ForEach(Array(zip(followers.indices, followers), id: \.1) { index, follower in
FollowerCell(follower: follower)
.onAppear(perform: {
// Now we test our index against the total array count.
// You should also build a check that once you get an end of
// data response that you don't keep calling for more data.
if followers.count()/* - buffer*/ == index { // Place buffer here for earlier update.
getFollowers(userID: ...
}
})
.onTapGesture {
self.getFollowerDetails(userID: follower.login)
}// ON TAP GESTURE
}// FOR EACH
})// LAZYVGRID
When I originally answered this I built in a buffer of 20 items before the update was called. However, with further experimentation, I found that this is not necessary as the .onAppear
is triggered well before the view actually appears on screen. If you want to add a buffer to give you more time to download the data, all you have to do is subtract a given amount of items from the total count so the update is triggered earlier. I put a comment about it as to where to place it.
Upvotes: 3