CollectionView:scrollToItem、reloadData 和 Dispatch 队列
CollectionView: scrollToItem, reloadData, and Dispatch Queues
大家下午好,
我遇到了一个关于我一直关注的聊天应用程序教程的令人沮丧的错误,我想补救(该教程没有解决这个问题,因为我已经转换为 Swift 3 / Xcode8).让我试着描述一下问题:
两个用户之间的聊天记录利用 Firebase 数据库来存储和检索他们之间的对话消息。我正在使用 collectionView 来显示对话。我想在聊天日志中实现一个功能,这样当用户选择要查看的对话时,它会滚动到 latest/last "message" 以便用户轻松继续对话。
这里是聊天记录控制器当前的相关代码:
func observeMessages() {
guard let uid = FIRAuth.auth()?.currentUser?.uid, let toId = user?.id else {
return
}
let userMessagesRef = FIRDatabase.database().reference().child("user-messages").child(uid).child(toId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let messageId = snapshot.key
let messagesRef = FIRDatabase.database().reference().child("messages").child(messageId)
messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String: AnyObject] else {
return
}
self.messages.append(Message(dictionary: dictionary))
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}, withCancel: nil)
}, withCancel: nil)
}
我遇到的问题是关于要滚动到 collectionView 的无效 indexPath。使用 print 语句,我发现 reloadData() 被调用了很多,这只是我从调试控制台假设 indexPath 不能 "update" 或 "keep up" 的值。
我是 GCD 的新手(我只被告知 UI 的更新应该始终在主线程上完成),并且想知道我的问题的答案是否在于设置适当的synchronous/asynchronous 执行,或 serial/concurrent 排队。
Ex// 使用后台线程获取会话消息并更新 indexPath,同时主线程异步重新加载数据和滚动条。
我不确定,但如果有人可以阐明这一点,或者为我指明正确的方向,我将不胜感激。非常感谢。
如果你的collectionView基于
self.messages array,
放
self.messages.append(Message(dictionary: dictionary))
也进入主线程。
用于主线程表(collectionView 或 tableView)的所有数据列表必须仅在主线程上进行管理。
大家下午好,
我遇到了一个关于我一直关注的聊天应用程序教程的令人沮丧的错误,我想补救(该教程没有解决这个问题,因为我已经转换为 Swift 3 / Xcode8).让我试着描述一下问题:
两个用户之间的聊天记录利用 Firebase 数据库来存储和检索他们之间的对话消息。我正在使用 collectionView 来显示对话。我想在聊天日志中实现一个功能,这样当用户选择要查看的对话时,它会滚动到 latest/last "message" 以便用户轻松继续对话。
这里是聊天记录控制器当前的相关代码:
func observeMessages() {
guard let uid = FIRAuth.auth()?.currentUser?.uid, let toId = user?.id else {
return
}
let userMessagesRef = FIRDatabase.database().reference().child("user-messages").child(uid).child(toId)
userMessagesRef.observe(.childAdded, with: { (snapshot) in
let messageId = snapshot.key
let messagesRef = FIRDatabase.database().reference().child("messages").child(messageId)
messagesRef.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String: AnyObject] else {
return
}
self.messages.append(Message(dictionary: dictionary))
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}, withCancel: nil)
}, withCancel: nil)
}
我遇到的问题是关于要滚动到 collectionView 的无效 indexPath。使用 print 语句,我发现 reloadData() 被调用了很多,这只是我从调试控制台假设 indexPath 不能 "update" 或 "keep up" 的值。
我是 GCD 的新手(我只被告知 UI 的更新应该始终在主线程上完成),并且想知道我的问题的答案是否在于设置适当的synchronous/asynchronous 执行,或 serial/concurrent 排队。
Ex// 使用后台线程获取会话消息并更新 indexPath,同时主线程异步重新加载数据和滚动条。
我不确定,但如果有人可以阐明这一点,或者为我指明正确的方向,我将不胜感激。非常感谢。
如果你的collectionView基于
self.messages array,
放
self.messages.append(Message(dictionary: dictionary))
也进入主线程。
用于主线程表(collectionView 或 tableView)的所有数据列表必须仅在主线程上进行管理。