如何在滚动到顶部的同时对从 firestore 获取的聊天进行分页,同时添加侦听器



我是 swift 的新手,通过观看教程成功地构建了一个聊天应用程序。现在,当我单击用户时,它会将我带到聊天控制器,并且它能够成功获取消息。一切正常。我需要实现的是能够在滚动到顶部时对聊天进行分页。现在,每次打开聊天室时,我都会获取所有消息。

我确实查看了有关如何执行此操作的谷歌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是我的目标是进行分页的地方。

我自己为分页奋斗了很长时间,因为当用户到达底部时,每个人都加载了更多,这很容易,您可以在 stackoverflow 上找到解决方案,但没有人实现加载之前的事情(意思是当用户滚动到顶部并拉取以刷新较旧的消息时,在聊天中您必须首先显示最新消息,然后显示较旧的消息,并且您的滚动也会向后倒退作为最新消息消息位于底部,最旧的位于顶部(。 经过如此多的努力,我发现这个解决方案运行良好。

首先,您正在实现快照侦听器,它将为您提供该表(文档(中的所有消息。你必须稍微改变一下。第一次进入聊天屏幕时忽略所有数据(我知道它看起来很愚蠢,但它有效(。新消息需要 Cuz 快照侦听器,否则不会收到最新消息。

然后在文档上进行分页。这是我的解决方案字段名称可以变化。

另外,我正在使用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
}
}
}

最新更新