UITableView分页,将显示无限加载

Posted

技术标签:

【中文标题】UITableView分页,将显示无限加载【英文标题】:UITableView pagination, willDisplay loading infinitely 【发布时间】:2018-10-24 16:46:39 【问题描述】:

我正在尝试在我的应用程序中实现聊天功能,该功能将模仿 iMessages 的拉动以加载更多消息。我的 API 在每次调用中发送 20 条消息以及 pageIndex 和其他值以跟踪页面和消息。

我正在使用 TableView willDisplay 实现分页并拉动刷新功能。

我无法添加正确的逻辑来在 willDisplay 中加载更多消息,它会进入无限循环。任何人都可以通过查看下面的代码指出我正确的方向吗?

import UIKit

class ChatViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate  

@IBOutlet weak var messagesTable: UITableView!
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!

var messages: Messages!
var messageArray = [Message]()

// Pagination
var isLoading = false
var pageSize = 10
var totalPages: Int!
var currentPage: Int!

// Pull To Refresh
let refreshControl = UIRefreshControl()

override func viewWillAppear(_ animated: Bool)
    super.viewWillAppear(animated)

    activityIndicator.startAnimating()

    fetchMessages(page: 1, completed: 

        self.totalPages = self.messages.pageCount
        self.currentPage = self.messages.currentPage

        // Sort message by ID so that latest message appear at the bottom.
        self.messageArray = self.messages.messages!.sorted(by: $0.id! < $1.id!)

        self.messagesTable.reloadData()

        // Scroll to the bottom of table
        self.messagesTable.scrollToBottom(animated: false)

        self.activityIndicator.stopAnimating()
    )



// MARK: - Table view data source
func numberOfSections(in tableView: UITableView) -> Int 
    return 1


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return messageArray.count


func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 

    if !self.isLoading && indexPath.row == 0 

        self.isLoading = true

        fetchMessages(page: self.currentPage, completed: 

            if self.currentPage == 0 
                self.messageArray.removeAll()
            

            self.messageArray.append(contentsOf: self.messages!.messages!)
            self.messageArray = self.messageArray.sorted(by: $0.id! < $1.id!)

            self.messagesTable.reloadData()

            // Scroll to the top
            self.messagesTable.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true)

            self.currentPage = self.currentPage + 1
        )

        self.isLoading = false
    



func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    guard let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath) as? MessageCell else 
        fatalError("Can't find cell")
    

    return cell



private func fetchMessages(page: Int, completed: @escaping () -> ())

    guard let url = URL(string: "http://example.com/....") else  return 

    URLSession.shared.dataTask(with: url)  (data, response, error) in
        if error != nil 
            print("Error in fetching data..........")
            print(error!.localizedDescription)
        

        guard let data = data else  return 
        if let str = String(data: data, encoding: .utf8)  print(str) 

        do 
            let resultData = try JSONDecoder().decode(messagesStruct.self, from: data)

            DispatchQueue.main.async 
                print("DispatchQueue.main.async")
                self.messages = resultData.data!

                completed()
            

         catch let jsonError 
            print(jsonError)
        
    .resume()


//Pull to refresh
@objc func refresh(_ refreshControl: UIRefreshControl) 

    fetchMessages(completed: 
        self.messagesTable.reloadData()
    )

    refreshControl.endRefreshing()

【问题讨论】:

【参考方案1】:

willDisplayCell 不是检查 tableView 是否真的滚动到底部的安全位置,而是使用 scrollViewDelegate

func scrollViewDidScroll(_ scrollView: UIScrollView) 
        if !self.isLoading && (messagesTable.contentOffset.y >= (messagesTable.contentSize.height - messagesTable.frame.size.height)) 
            self.isLoading = true

            fetchMessages(page: self.currentPage, completed: [weak self]

                guard let strongSelf = self else 
                    return
                
                strongSelf.isLoading = false
                if strongSelf.currentPage == 0 
                    strongSelf.messageArray.removeAll()
                

                strongSelf.messageArray.append(contentsOf: strongSelf.messages!.messages!)
                strongSelf.messageArray = strongSelf.messageArray.sorted(by: $0.id! < $1.id!)

                strongSelf.messagesTable.reloadData()

                strongSelf.currentPage = strongSelf.currentPage + 1
            )
        
    

希望对你有帮助

【讨论】:

Tx,我试过了。第一个问题是 scrollViewDidScroll 在调用任何 viewXYZAppear 方法之前就被调用了,它会进入 fetch & reload 表。这正常吗? @davems :我个人更喜欢这样,而不是在 viewWillAppear 中手动调用 fetchMessages(page: 1,,但是很难相信当 tableView 条件中没有数据时 (messagesTable.contentOffset.y &gt;= (messagesTable.contentSize.height - messagesTable.frame.size.height) 会通过 @davems :但是如果由于任何奇怪的原因发生这种情况,它将使您免于手动调用 fetchMessages(page: 1, 这更清洁 :) 它有点创建自启动加载器机制 :) 但我几乎不怀疑 scrollViewDidScroll如果它一开始就没有内容,将永远被调用,即使它被调用也非常怀疑它是否会通过条件。请放一个断点再检查一遍 这是在调用任何 viewXXXApepar 之前的第一次打印:isLoading: false messagesTable.contentOffset.y: 0.0 messagesTable.contentSize.height: 0.0 messagesTable.frame.size.height: 590.0 条件变为是的。 @davems : 嗯,有趣,给我一秒钟 :) 但请相信我注释掉你的 viewWillAppears fetchMessages(page: 1,,你会发现你有一个很好的分页系统,它可以自己启动:P 虽然启动本身不是直到那时的意图让我检查一下我是如何解决的

以上是关于UITableView分页,将显示无限加载的主要内容,如果未能解决你的问题,请参考以下文章

FMDB 和 UITableView

当没有更多数据时如何停止无限循环(分页)

带缓存的无限/分页滚动

来自 url 的 UIImage 需要很长时间才能快速加载

Magento:自定义集合中缺少分页

在 iOS 7 上的 UITableView 上指示加载活动的正确方法是啥?