如何在滚动到顶部同时添加侦听器时对从 Firestore 获取的聊天进行分页
Posted
技术标签:
【中文标题】如何在滚动到顶部同时添加侦听器时对从 Firestore 获取的聊天进行分页【英文标题】:How to paginate chat fetched from firestore while scrolling to the top while also adding a listener 【发布时间】:2019-10-11 18:07:50 【问题描述】:我是 swift 新手,我通过观看教程成功地构建了一个聊天应用程序。现在,当我单击一个用户时,它会将我带到聊天控制器,并且它能够成功获取消息。一切正常。我需要实现的是能够在我滚动到顶部时对聊天进行分页。现在每次打开聊天室时我都会获取所有消息。
我确实查看了有关如何执行此操作的 google firestore 文档,但对于我的生活,我无法将其拼凑起来。对它在理论上是如何工作的有一个公平的想法。但是由于我添加了快照侦听器,所以有点令人困惑。
也不知道如何配置滚动视图在到达顶部时加载
class ChatLogController: UICollectionViewController, UICollectionViewDelegateFlowLayout
//MARK: Initialize
fileprivate let connect: Connect
var listener: ListenerRegistration?
var currentUser: User?
init(connect: Connect)
self.connect = connect
super.init()
//MARK: ****View Did Load****
override func viewDidLoad()
super.viewDidLoad()
fetchCurrentUser()
fetchPaginatedMessages()
setupView()
//MARK: Fetch Current User
fileprivate func fetchCurrentUser()
//Some code
print("Current User Fetched Successfully")
self.currentUser = User(dictionary: data)
//MARK: Setup View
//Some code that fetches the front end
//MARK: FetchPaginatedMessages - Code does not contain attempted pagination
fileprivate func fetchPaginatedMessages()
print("Fetching Messages")
guard let cUid = Auth.auth().currentUser?.uid else return
let query = Firestore.firestore().collection("matches").document(cUid).collection(connect.uid).order(by: "Timestamp")
listener = query.addSnapshotListener (querySnapshot, error) in
if let error = error
print("There was an error fetching messages", error.localizedDescription)
return
querySnapshot?.documentChanges.forEach( (change) in
if change.type == .added
let dictionary = change.document.data()
self.items.append(.init(dictionary: dictionary))
print("ARCHID ---- FIRESTORE HAS BEEN CONTACTED FETCHING MESSAGES")
)
self.collectionView.reloadData()
self.collectionView.scrollToItem(at: [0, self.items.count - 1], at: .bottom, animated: true)
print("Fetched messages")
包括代码的主要部分。为了便于阅读,省略了不相关的代码。 func fetchPaginatedMessages 是我打算进行分页的地方。
【问题讨论】:
分页是 Firestore 可以通过使用 .startAt 和/或 .endAt(查询游标)来完成。入门指南Paginate data with query cursors 中有一些非常好的文档。您还可以将文档快照传递给游标以定义查询游标的起点或终点。我没有看到您在代码中实际在哪里进行 Firestore 分页 - 也许我忽略了它或误解了您的要求。你能澄清一下吗? 哦... Ray Wenderlich 教程是必读的。 UITableView Infinite Scrolling Tutorial 如果你想了解无限滚动。 您好,Jay 感谢您的评论。我实际上没有做任何分页。我刚刚命名了这个函数,以表明它是用来进行分页的。是的,在我发布到 *** 之前,我确实阅读了链接中的文章,但我无法理解它是如何工作的,尤其是当我使用侦听器时 嗯。您的问题是如何分页,这意味着您在问如何分页....但您没有进行分页?意思是你不想做分页或者你不知道如何去实现代码? @Jay 我的意思是我试图在 firebase 文档中执行代码,但无法让它工作。另外,当我位于滚动顶部时,我无法理解如何触发分页。所以我最终只是发布了我如何接收所有消息以及如何将其转换为分页。 【参考方案1】:我自己为分页挣扎了很长时间,因为当用户到达底部时每个人都在加载更多,这很容易,你可以在 *** 上找到解决方案,但是没有人实现加载之前的事情(意味着当用户滚动到顶部时并拉动以刷新旧消息,因为在聊天中您必须先显示最新消息,然后再显示旧消息,并且您的滚动也会向后滚动,因为最新消息在底部,最旧的消息在顶部)。 经过这么多努力,我发现这个解决方案运行良好。
首先,您正在实现快照侦听器,它将为您提供该表(文档)中的所有消息。你必须稍微改变一下。首次进入聊天屏幕时忽略所有数据(我知道它看起来很愚蠢,但它有效)。因为新消息需要快照侦听器,否则您不会收到最新消息。
然后对文档进行分页。这是我的解决方案字段名称可能会有所不同。
另外,我正在使用 MessageKit 来显示聊天 UI,您可以进行相应更改。 这是我用于分页的聊天视图控制器,您可以根据需要进行更改。 当用户触发拉动以自动刷新时,我的代码会拉出较旧的消息,您可以找到无限滚动到顶部的代码以满足您的要求。 谢谢。
class ChatViewController1: MessagesViewController
// MARK: - Variables -
var messages: [Message] = []
var pageSize: Int = 25
var handles: [ListenerRegistration] = []
var lastSnapShot: QueryDocumentSnapshot?
var msgNode = "Your Table Name"
private(set) lazy var refreshControl: UIRefreshControl =
let control = UIRefreshControl()
control.addTarget(self, action: #selector(loadMoreMessages), for: .valueChanged)
return control
()
// MARK: - Life Cycle -
override func viewDidLoad()
super.viewDidLoad()
messagesCollectionView.refreshControl = refreshControl
self.setupChat()
override func viewWillDisappear(_ animated: Bool)
super.viewWillDisappear(animated)
for item in handles
item.remove()
// MARK: - Setup Methods -
@objc func loadMoreMessages()
self.getChat(lastSnapShot)
func setupChat()
FIRDBManager.shared.db
.collection(self.msgNode)
.order(by: "messageTime", descending: false).addSnapshotListener(includeMetadataChanges: true) snapshot, error in
if let error = error
print("There was an error fetching messages", error.localizedDescription)
return
// I am checking if messages already exist then it means this is new message or earlier message updated / deleted so handle it that way.
if self.messages.count > 0
snapshot?.documentChanges.forEach( (change) in
let dictionary = change.document.data()
let msg = Message(fromDictionary: dictionary)
if change.type == .added
if !self.messages.contains(msg)
self.messages.append(msg)
else if change.type == .removed
self.messages.removeAll(where: $0.messageId == msg.messageId )
else if let index = self.messages.firstIndex(of: msg)
self.messages[index] = msg
)
DispatchQueue.main.async
self.messagesCollectionView.reloadData()
self.messagesCollectionView.scrollToLastItem()
self.getChat(nil)
func getChat(_ snap: QueryDocumentSnapshot?)
let query: Query
if let _ = snap
query = FIRDBManager.shared.db
.collection(self.msgNode)
.order(by: "messageTime", descending: false)
.end(at: [messages.first!.messageTime!])
.limit(toLast: self.pageSize)
//end(atDocument: s)
else
query = FIRDBManager.shared.db
.collection(self.msgNode)
.order(by: "messageTime", descending: false)
.limit(toLast: self.pageSize)
query.getDocuments (snapshot, err) in
if let e = err
print("Error fetching document: \(e.localizedDescription)")
self.refreshControl.endRefreshing()
else if snapshot!.isEmpty
print("Document is empty")
self.refreshControl.endRefreshing()
else
self.updateMessages(snapshot!)
fileprivate func updateMessages(_ snapshot: QuerySnapshot)
if snapshot.documents.count <= 1
self.refreshControl.endRefreshing()
return
var allMessages: [Message] = []
snapshot.documents.forEach (item) in
if item.documentID != "DeviceIds"
let msg = Message(fromDictionary: item.data())
allMessages.append(msg)
DispatchQueue.main.async
if self.messages.count > 0
self.messages.insert(contentsOf: allMessages, at: 0)
self.messagesCollectionView.reloadDataAndKeepOffset()
self.refreshControl.endRefreshing()
else
self.messages = allMessages
self.messagesCollectionView.reloadData()
self.messagesCollectionView.scrollToLastItem()
self.lastSnapShot = snapshot.documents.last
【讨论】:
以上是关于如何在滚动到顶部同时添加侦听器时对从 Firestore 获取的聊天进行分页的主要内容,如果未能解决你的问题,请参考以下文章
添加滚动到顶部按钮(Ionic 2 | Typescript)