我使用新的Firebase制作了一个简单的社交媒体,并且我成功地将带有数据库和图像的字符串与存储一起保存,但是当它将数据检索回到表格时,会发生异常的事情!
所有返回的图像随机显示并不断移位,但其他部分显示完美或当我使用返回posts.count tableView显示没有帖子。
希望有人可以给我一些建议
import UIKit
import Firebase
import FirebaseStorage
class timelineTableViewController: UITableViewController {
var posts = [Post]()
var databaseRef: FIRDatabaseReference!
var storageRef: FIRStorageReference!
override func viewDidLoad() {
super.viewDidLoad()
databaseRef = FIRDatabase.database().reference()
storageRef = FIRStorage.storage().reference()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return posts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "postCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)as! timelineTableViewCell
let userPostRef = self.databaseRef.child("posts")
userPostRef.observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary{
let myPost = Post(data: postAdd)
self.posts.insert(myPost, atIndex:0)
cell.usernameLabel.text = self.posts[indexPath.row].username
cell.postText.text = self.posts[indexPath.row].postText
cell.timeLabel.text = self.posts[indexPath.row].time
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let postPhoto = UIImage(data: data!)
cell.postPhoto.image = postPhoto
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
cell.userPhoto.image = userPhoto
})
})
}
})
return cell
}
}
答案 0 :(得分:6)
此处最佳做法是在ViewDidLoad方法中填充tableView数据源(posts数组)。然后你的tableView没有尝试下拉数据,因为它更新。
此外,由于您要添加.childAdded观察器,因此您的应用程序将收到有关firebase的任何添加的通知,因此当发生这种情况时,只需将新数据添加到posts数组并刷新tableView。
没有理由不断地从Firebase中提取数据,因为它一直是静态的,直到它发生变化。因此,加载一次,然后等待这些更改 - 您可能需要考虑添加.childChanged事件(并删除),以防有人更新他们的图片。
来自Firebase的Firechat应用是了解最佳实现方式的绝佳资源 - 如果您遵循该设计模式,您可以避免所有网络问题而不必担心单独的异步调用。
您可以简单地依靠Firebase来照顾自己。
编辑......好吧,虽然这是一个Firechat链接,Obj-C代码似乎丢失了(感谢Firebase。呃)
所以 - 鉴于此,请参阅我对this question的回答,因为它有您所需代码的模式。
答案 1 :(得分:2)
您遇到了线程问题。
线程和您的用户界面 如果您的应用程序具有图形用户界面,建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。此方法有助于避免与处理用户事件和绘制窗口内容相关的同步问题。某些框架(如Cocoa)通常需要这种行为,但即使对于那些不这样做的框架,将此行为保留在主线程上也具有简化管理用户界面的逻辑的优势。
在您的代码中,您异步获取数据,这可能会在与主线程不同的线程上获取数据,为避免这种情况,您可以将代码包装在dispatch_async
块中的代码中,如下所示:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "postCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)as! timelineTableViewCell
let userPostRef = self.databaseRef.child("posts")
userPostRef.observeEventType(.ChildAdded, withBlock: {(snapshot) in
if let postAdd = snapshot.value as? NSDictionary{
let myPost = Post(data: postAdd)
self.posts.insert(myPost, atIndex:0)
//Dispatch the main thread here
dispatch_async(dispatch_get_main_queue()) {
cell.usernameLabel.text = self.posts[indexPath.row].username
cell.postText.text = self.posts[indexPath.row].postText
cell.timeLabel.text = self.posts[indexPath.row].time
}
let url = snapshot.value?["postPhoto"] as! String
let userPhotoUrl = snapshot.value?["userPhoto"] as! String
FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
dispatch_async(dispatch_get_main_queue()){
let postPhoto = UIImage(data: data!)
cell.postPhoto.image = postPhoto
}
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
//Dispatch the main thread here
dispatch_async(dispatch_get_main_queue()) {
let userPhoto = UIImage(data: data!)
cell.userPhoto.image = userPhoto
}
})
})
}
})
return cell
}
请注意,将这样的网络代码和UI代码混合在一起并不是最好的选择。你可以做的是有一个函数加载/监视你的端点,然后将它添加到一个数组,然后调用tableView.reloadData()
并更新视图。
如果您想了解更多关于线程和GCD的知识,请查看此WWDC session