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

New Firebase retrieve data and put on the tableView (Swift)

我用新的 Firebase 做了一个简单的社交媒体,我成功地用数据库保存了字符串,用存储保存了图像,但是当要将数据检索回 tableView 时,发生了不寻常的事情!

所有检索回来的图像随机显示并不断移动,但其他部分显示完美或当我使用 return posts.count tableView 时没有显示 post.

希望有好心人能给我一些建议

    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
    }

    }

您遇到线程问题。

From the apple docs:

Threads and Your User Interface If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread has the advantage of simplifying the logic for managing your user interface.

在您的代码中,您正在异步获取数据,这可能会在与主线程不同的线程上获取数据,为避免这种情况,您可以将设置 UI 的代码包装在 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

此处的最佳做法是在 ViewDidLoad 方法中填充 tableView 数据源(帖子数组)。那么您的 tableView 不会在刷新时尝试下拉数据。

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

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

Firebase 的 Firechat 应用程序是了解执行此操作的最佳方法的重要资源 - 如果您遵循该设计模式,则可以避免所有网络问题,而不必担心单独的异步调用。

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

编辑...好吧,虽然那是一个 Firechat link,但似乎缺少 Obj-C 代码(感谢 Firebase。呃)

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