【问题标题】:How do you tell when the user has scrolled to the bottom of a LazyVGrid?您如何判断用户何时滚动到 LazyVGrid 的底部?
【发布时间】:2021-10-07 01:00:53
【问题描述】:

我对 SwiftUI 还很陌生,我正在尝试找出一个我似乎无法解决的问题。我正在构建一个基于 GitHub API 的应用程序。在其中一个屏幕中,我显示了特定用户拥有的所有关注者。

我想要完成的是,我想知道用户何时滚动到关注者列表的底部,因为我目前只从服务器获取 100 条记录。

我如何知道用户何时滚动到这 100 个关注者的底部,以便我可以在第二页和后续页面中发起另外 100 条记录的请求?

我在 google 上搜索过,看到了有关 ScrollViewReader、GeometryReader 的信息,但我不知道如何解决这个问题。这是此特定屏幕中的代码:

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

【问题讨论】:

  • 要回答您的问题,您需要处理GeometryReader,当我们处于所有关注者的底部时,它需要一些行代码来通知,但这是不必要的,因为LazyVGrid 足够聪明调用所需的数据!你不应该关心它!如果没有 LazyVGrid 存在,你会做这样的事情!
  • 抱歉,您的评论完全不清楚。
  • 你的意思是完全不清楚?
  • 是的,如果您能描述更容易理解的步骤。

标签: swiftui


【解决方案1】:

你不需要知道你什么时候在LazyVGrid的底部,你需要知道你什么时候数据用完了。您需要您在列表中的位置的索引,并将其与数组的计数进行比较。另外,我不得不使用一些伪代码,因为我没有你所有的类型:

            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

当我最初回答这个问题时,我在调用更新之前建立了一个包含 20 个项目的缓冲区。然而,通过进一步的实验,我发现这不是必需的,因为.onAppear 在视图实际出现在屏幕上之前就被触发了。如果您想添加一个缓冲区以便有更多时间下载数据,您所要做的就是从总计数中减去给定数量的项目,以便更早触发更新。我对它的放置位置发表了评论。

【讨论】:

  • 已编辑以更正ForEach 中的id:
  • 感谢您的评论!我会尝试并报告!
  • 它似乎工作.. 谢谢!
  • 那么,zip 方法只允许您将索引附加到关注者数组中的特定项目?这是一个聪明的主意!
  • 它本质上创建了一个元组数组。一个是数组的元素,一个是它的索引。对id: 的编辑是告诉ForEach 以元素的id 为基础,而不是索引。请参阅this answer 以获得更完整的解释。我在遇到.enumerated 的问题时偶然发现了它。
猜你喜欢
  • 2010-11-06
  • 1970-01-01
  • 2011-09-03
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多