2

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

所有检索回来的图像都随机显示并不断移动,但其他部分显示完美,或者当我使用 return 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
    }

    }
4

2 回答 2

6

在 ViewDidLoad 方法中填充 tableView 数据源(posts 数组)的最佳实践。然后,您的 tableView 在刷新时不会尝试下拉数据。

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

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

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

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

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

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

于 2016-06-18T13:24:25.927 回答
2

您遇到线程问题。

来自苹果文档

线程和您的用户界面 如果您的应用程序具有图形用户界面,建议您接收与用户相关的事件并从应用程序的主线程启动界面更新。这种方法有助于避免与处理用户事件和绘制窗口内容相关的同步问题。一些框架,例如 Cocoa,通常需要这种行为,但即使对于那些不需要这种行为的框架,将这种行为保留在主线程上具有简化管理用户界面的逻辑的优势。

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

于 2016-06-18T12:50:50.137 回答