何时在 tableView 单元中分离 firebase 侦听器?
Posted
技术标签:
【中文标题】何时在 tableView 单元中分离 firebase 侦听器?【英文标题】:When to detach firebase listeners in tableView cell? 【发布时间】:2019-03-19 16:22:03 【问题描述】:在聊天应用程序中,为了跟踪参与聊天的每个用户的每个聊天的最后一条消息和未读消息,当 tableView 单元出列时,我在单元上附加了一个 .childChanged
侦听器。当侦听器被触发时,我会更新每一行的聊天 label.text 以进行相应的聊天。
我应该什么时候删除这些听众,或者在我的情况下更新单元格中的聊天的最佳做法是什么?
程序的流程是什么? 1.下载当前用户 2. 下载当前用户聊天ID 3.为每个聊天ID下载聊天 4. 使用聊天填充 tableView 5. 在每个单元格中观察 childChanged at 聊天/chat.chatUID/currentUserUID/.observe(.childChanged) 6.如果“unreadMessagesCount”发生了变化,在单元格上更新它
class ChatTableViewCell: UITableViewCell
@IBOutlet weak var lastMessageLabel: UILabel!
var chat: Chat!
didSet
self.updateUI()
func updateUI()
self.chat.observeChildChanged(chat: self.chat, currentUserUID:user.userUID) (lastMessage, unreadMessagesCount) in
if !lastMessage.isEmpty
self.lastMessageLabel.text = lastMessage
if unreadMessagesCount > 0
self.lastMessageLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
self.chatUnreadMessagesCount.text = "\(unreadMessagesCount)"
else
self.lastMessageLabel.font = UIFont.systemFont(ofSize: 15.0)
self.chatUnreadMessagesCount.text = ""
class MessagesViewController: UITableViewController
override func viewDidLoad()
//observe ~/users/uid
DDatabaseRReference.users(uid: uid).reference().observeSingleEvent(of: .value, with: (snapshot) in
guard snapshot.exists() else return
if let userDict = snapshot.value as? [String : Any]
self.currentUser = UserModel(dictionary: userDict)
self.userWasDownloaded = true //this will trigger the setter and start downloading chatId's of current user
)
var userWasDownloaded: Bool
get
return true
set
self.fetchChatsIdsOf(currentUser: self.currentUser)
self.tableView.reloadData()
func fetchChatsIdsOf(currentUser: UserModel)
//get chatIds of currentUser from ~/users/currentUser.userUID/chatIds
DDatabaseRReference.users(uid: currentUser.userUID).reference().child("chatIds").observe(.childAdded, with: (snapshot) in
let chatUID = snapshot.key
if !self.chatIdsDownloaded.contains(chatUID)
self.chatIdsDownloaded.append(chatUID)
)
//after chatIdsDownloaded is set,
//download the new chat for the last chat appended to chatIdsDownloaded array
var chatIdsDownloaded = [String]()
didSet
guard let chatID = chatIdsDownloaded.last else return
self.downloadNewChat(chatID: chatID)
func downloadNewChat(chatID: String)
DDatabaseRReference.chats.reference().child(chatID).observeSingleEvent(of: .value, with: (snapshot) in
......
self.currentUserChats.insert(chatChecked, at: 0)
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewCell", for: indexPath) as! ChatTableViewCell
cell.chat = currentUserChats[indexPath.row]
return cell
chats // <- all chats in the app for all users in the app
-LOMVtcjOEOu2p1apMKV
chatUID: "-LOMVtcjOEOu2p1apMKV"
isGroupChat: true
lastMessage: "Katherine Gregory has joined the group"
lastUpdate: 1539761870.2237191
+users
IN4pgCS5NqQZZLpdmoz1KeDiFqj2
fcmToken: ""
firstName: "Alex"
userUID: "IN4pgCS5NqQZZLpdmoz1KeDiFqj2"
unreadMessagesCount: 5
users // <- all users in the app
IN4pgCS5NqQZZLpdmoz1KeDiFqj2
+chatIds
-LOMVtcjOEOu2p1apMKV: true
- name: ""
- email: ""
...etc
【问题讨论】:
我不建议在单元格中使用侦听器或那种逻辑 - 这可能会使您的 UI 滞后。 IMO tableViews 是由数据源支持的 UI 元素。当数据发生变化时,更新数据源,然后重新加载 tableView,然后更新 UI。您的 viewController 会将 .childAdded、.childChanged 和 .childRemoved 观察者添加到 Firebase。当发生更改时,您的类会收到通知,您可以相应地更新 dataSource 数组。我建议使用一个名为 is_read 的子节点,它在未读取时为 false,因此您可以轻松跟踪已读/未读消息。 当您开始收听.onSnapshot
时,您会下载现有的内容,然后当且仅当它(收听的路径)发生更改时,您才会获得更新。因此,无需“下载”。只需 .onSnapshot
您想要传递给您的 UI/应用程序的东西。
@Jay 我知道数据源应该支持 UI,但我不知道从哪里开始。我确实在users/currentUserUID
上附加了.childAdded、.childChanged、.childRemoved,这样我就可以读取/删除/写入currentUser 的chatUID。但是,一旦我阅读了 currentUser 的 chatUID,我需要下载存储在chats/chatUID
的聊天记录。现在,问题是我需要在每次下载的聊天中观察 .childChanged。 unreadMessagesCount: 5
是 chats/chatUID/users/currentUserUID/ unreadMessagesCount: 5.
的孩子
我们不知道结构,但应该是向 viewController 中的每个聊天(而不是每个单元格)添加 .childAdded 观察者。这将在每个聊天中读取,然后通知您任何新添加的聊天。我会首先让您的 tableView 从 dataSource 数组中读取它的数据,在 viewController 中有一个处理 .childAdded 事件的函数,该事件会在初始化时使用现有的聊天信息相应地更新您的 dataSource,并在这些事件发生时添加新的聊天信息。例如,聊天 3 有新聊天,事件将触发,更新聊天 3 的 dataSource 数组,重新加载 tableView。
我明白了,我会开始工作的。谢谢
【参考方案1】:
您可以检查是否为单元添加了 2 个或更多观察者。
在此处添加断点或 print():
self.chat.observeChildChanged(chat: self.chat, currentUserUID: user.userUID) (lastMessage, unreadMessagesCount) in
//breakpoint or print("observeChildChanged")
...
请重复使用您的手机。 发送新消息。
如果您有 2 条或更多消息,则表示您没有只设置一个观察者。
也许这种方法并不完美,但它可以帮助你(在添加新的之前删除旧的观察者):
var chat: Chat!
didSet
self.removeOldObserver()
self.updateUI()
func removeOldObserver()
...
【讨论】:
【参考方案2】:正如 Jay 所建议的,我在新下载的每个聊天中都附上了 .childChanged
观察者。
但是,如果我转到 firebase 控制台并更新 ref
路径上的孩子的值,则不会总是触发 childChanged 观察者。有时有效,有时无效 可能是什么问题?
我在所有行上都使用了断点,当数据库中的值发生变化时,它们都没有被命中。
示例:name:
"Alex"
更改为:名称:“约翰”
更新
这个答案是正确的,我忘记删除我之前在prepareForReuse()
中分离监听器的实现@
var currentUserChats = [Chat]()
didSet(newValue)
attachChildChangedObserverOn(chat: newValue)
var observersArray = [String: UInt]() // chatUID:handle
func attachChildChangedObserverOn(chat: Chat)
var handle: UInt = 0
let ref = DDatabaseRReference.chats.reference().child(chat.chatUID).child("users").child(currentUser.userUID)
handle = ref.observe(.childChanged, with: [weak self] (snapshot) in
self?.observersArray[chat.chatUID] = handle
print("snapshot.value is \(snapshot.value) and snapKey is \(snapshot.key)")
guard snapshot.exists() else return
let chatChanged = chat
var lastMessage = ""
var unreadMessagesCount = 0
var lastUpdate = 0.0
switch snapshot.key
//case....
)
【讨论】:
以上是关于何时在 tableView 单元中分离 firebase 侦听器?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 ViewController 中分离(推送)到 NavigationController 中嵌入的另一个 ViewController?
将 UITableView 委托和数据源从 tableViewController 中分离出来