新的Firebase检索数据并放在tableView(Swift)上

时间:2016-06-18 08:59:21

标签: swift firebase tableview firebase-realtime-database firebase-storage

我使用新的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
    }

    }

2 个答案:

答案 0 :(得分:6)

此处最佳做法是在ViewDidLoad方法中填充tableView数据源(posts数组)。然后你的tableView没有尝试下拉数据,因为它更新。

此外,由于您要添加.childAdded观察器,因此您的应用程序将收到有关firebase的任何添加的通知,因此当发生这种情况时,只需将新数据添加到posts数组并刷新tableView。

没有理由不断地从Firebase中提取数据,因为它一直是静态的,直到它发生变化。因此,加载一次,然后等待这些更改 - 您可能需要考虑添加.childChanged事件(并删除),以防有人更新他们的图片。

来自Firebase的Firechat应用是了解最佳实现方式的绝佳资源 - 如果您遵循该设计模式,您可以避免所有网络问题而不必担心单独的异步调用。

您可以简单地依靠Firebase来照顾自己。

编辑......好吧,虽然这是一个Firechat链接,Obj-C代码似乎丢失了(感谢Firebase。呃)

所以 - 鉴于此,请参阅我对this question的回答,因为它有您所需代码的模式。

答案 1 :(得分:2)

您遇到了线程问题。

From the apple docs

  

线程和您的用户界面   如果您的应用程序具有图形用户界面,建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。此方法有助于避免与处理用户事件和绘制窗口内容相关的同步问题。某些框架(如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