您如何判断用户何时滚动到 LazyVGrid 的底部?

Posted

技术标签:

【中文标题】您如何判断用户何时滚动到 LazyVGrid 的底部?【英文标题】:How do you tell when the user has scrolled to the bottom of a 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 存在,你会做这样的事情! 抱歉,您的评论完全不清楚。 你的意思是完全不清楚? 是的,如果您能描述更容易理解的步骤。 【参考方案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 的问题时偶然发现了它。

以上是关于您如何判断用户何时滚动到 LazyVGrid 的底部?的主要内容,如果未能解决你的问题,请参考以下文章

检测用户何时使用 jQuery 滚动到 div 的底部

如何判断用户何时在 React-Native 中回答了 iOS 通知权限请求

jQuery scroll() 检测用户何时停止滚动

如何检测滚动到水平 UICollectionView 的末尾

如何检测用户何时尝试滑动“过去的 UIScrollView 内容偏移”?

在看到页脚之前检测用户何时滚动到像素