Получить изображение из хранилища firebase для отображения в tableView (Swift)

Вопрос о расширении от New Firebase извлекает данные и помещает в tableView (Swift)

Переместив код Firebase в viewDidLoad, вся текстовая часть сообщения может отображаться в настоящее время. Но я все еще не могу поместить изображение в tableView. Я проверил получение изображений, и это было успешно, я полагаю, что изображений, которые я извлекаю из Firebase, не было в массиве сообщений.

это мой код исправления:

  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()
    let userPostRef = self.databaseRef.child("posts")
    userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
        if let postAdd  = snapshot.value as? NSDictionary{

            let url = snapshot.value?["postPhoto"] as! String
            let userPhotoUrl = snapshot.value?["userPhoto"] as! String
            let myPost = Post(data: postAdd)

            FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
                let postPhoto = UIImage(data: data!)

                                    })
            FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
                let userPhoto = UIImage(data: data!)
                                })

            self.posts.insert(myPost, atIndex: 0)
            self.tableView.reloadData()

        }




})

}
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 {
    return posts.count


}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cellIdentifier = "postCell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)as! timelineTableViewCell
    //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
        cell.postPhoto.image = self.posts[indexPath.row].postPhoto
        cell.userPhoto.image = self.posts[indexPath.row].userPhoto


    }
    return cell
   }
   }

И моя структура Post надеюсь, что это может помочь!

struct  Post {
var username: String?
var postText: String?
var time: String?
var userPhoto: UIImage?
var postPhoto: UIImage?
var url: String?
var userPhotoUrl:String?
init(data: NSDictionary) {
   username = data["username"] as? String
   postText = data["postText"] as? String
    time = data["time"]as? String
    userPhoto = data["userPhoto"] as? UIImage
    postPhoto = data["postPhoto"] as? UIImage
    url = data["postPhoto"] as? String
    userPhotoUrl = data["userPhoto"] as? String
}

}

Надежда может получить некоторые советы!


person Johnny Hsieh    schedule 20.06.2016    source источник


Ответы (3)


Просто измените следующие методы

func downloadPost(){
    let userPostRef = self.databaseRef.child("posts")

    userPostRef.queryOrderedByChild("time").observeEventType(.ChildAdded, withBlock: {(snapshot) in
        if let postAdd  = snapshot.value as? NSDictionary {
            print("\(snapshot.value)")
            let url = snapshot.value?["postPhoto"] as! String
            let userPhotoUrl = snapshot.value?["userPhoto"] as! String

            var myPost = Post(data: postAdd) //variable change to "var" because going to modify

            let url2 = NSURL(string: url)  //postPhoto URL
            let data = NSData(contentsOfURL: url2!) // this URL convert into Data
            if data != nil {  //Some time Data value will be nil so we need to validate such things 
             myPost.postPhoto = UIImage(data: data!)
            }


            let url3 = NSURL(string: userPhotoUrl)  //userPhoto URL
            let data2 = NSData(contentsOfURL: url3!)  //Convert into data
            if data2 != nil  {  //check the data
            myPost.userPhoto = UIImage(data: data2!)  //store in image
            }




            self.posts.insert(myPost, atIndex: 0)  // then add the "myPost"  Variable
            print(self.posts.count)
        }
        self.tableView.reloadData()  // finally going to load the collection of data in tableview
    })


}

это занимает много времени загрузки. потому что преобразование изображения и сохранение его в основной очереди.

другие способы - сохранить URL-адрес и получить доступ к URL-адресу на дисплее таблицы. может применить асинхронную загрузку в изображении.

person HariKrishnan.P    schedule 08.07.2016

Похоже, проблема в том, что ваш пост никогда не получает информацию о фотографиях из-за того, что эти фотографии загружаются асинхронно.

Используя псевдокод, вы делаете следующее:

Get information from database
  Create post object
    Begin downloading post photo
      Populate post object with post photo
    Begin downloading user photo
      Populate post object with user photo
  Return post object

Происходит то, что объект сообщения возвращается до загрузки фотографий (поскольку вызов dataWithMaxSize:completion: является асинхронным). То, что вам нужно в конечном итоге сделать, больше похоже на:

Get information from database
  Create post object
    Begin downloading post photo, etc.
      Begin downloading user photo, etc.
        Return post object, now that it's fully populated

Это парадигма, широко известная как «ад обратных вызовов», поскольку асинхронный код вкладывается довольно глубоко, и его становится труднее читать и отлаживать. JavaScript изобрел Promises, чтобы справиться с этим, но, к сожалению, Swift не имеет первоклассной поддержки для чего-либо подобного (хотя guard — хороший шаг в этом направлении). Существуют библиотеки, такие как Bolts, которые используют асинхронную структуру, которая выглядит более синхронно, но опять же, код просто становится немного сложнее.

Есть вещи, которые вы можете сделать (добавление семафоров/мьютексов и ожидание завершения загрузки перед созданием объекта), но это довольно продвинутые концепции, и я бы освоил синхронное/асинхронное программирование, прежде чем пытаться это сделать.

person Mike McDonald    schedule 20.06.2016
comment
Спасибо @Mike за подробное описание моего вопроса. Теперь я понимаю проблему, почему изображения моих сообщений не могут отображаться. Поскольку я все еще новичок, не знаю, с чего начать, не могли бы вы дать мне дополнительные инструкции? Спасибо еще раз! - person Johnny Hsieh; 21.06.2016
comment
@mike Правильный способ, который вы описали, похоже, блокирует основной поток и неявно пользовательский интерфейс. Как мы должны загружать изображения, чтобы у пользователя не было плохого опыта? Я создаю приложение чата, и мне нужно загрузить из базы данных текст/изображения, видео... поэтому мои закрытия возвращаются до завершения загрузки, поэтому порядок загруженных сообщений нарушается, потому что текстовые сообщения добавляются в источник данных массив перед фотографиями, поэтому порядок полностью потерян. Что ты предлагаешь? Спасибо - person bibscy; 24.08.2018
comment
@bibscy мы выполняем загрузки в фоновом потоке по умолчанию, а затем вызываем обратные вызовы в основном потоке, поэтому мы не блокируем рендеринг пользовательского интерфейса, а помещаем вас в контекст для обновления пользовательского интерфейса, когда вам это нужно. Я бы рекомендовал установить изображение-заполнитель (например, значок загрузки), а затем правильно установить изображение при срабатывании обратного вызова. SDWebImage имеет эту возможность по умолчанию: github.com/rs/SDWebImage#how-to-use - person Mike McDonald; 25.08.2018

Удалите эти 2 строки:

userPhoto = data["userPhoto"] as? UIImage
postPhoto = data["postPhoto"] as? UIImage

Обновите этот код:

FIRStorage.storage().referenceForURL(url).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
     dispatch_async(dispatch_get_main_queue()) {
            myPost.postPhoto = UIImage(data: data!)
            self.tableView.reloadData()
     }

                                })
FIRStorage.storage().referenceForURL(userPhotoUrl).dataWithMaxSize(10 * 1024 * 1024, completion: { (data, error) in
     dispatch_async(dispatch_get_main_queue()) {
            myPost.userPhoto = UIImage(data: data!)
            self.tableView.reloadData()
            }
     })
person Prcela    schedule 05.07.2016
comment
Спасибо за ваш ответ @Prcela, я просто пробую ваш код, но все еще возвращаю ячейку сообщения, прежде чем закончить загрузку изображения. Это работает для меня. - person Johnny Hsieh; 05.07.2016
comment
удалите self.tableView.reloadData(), который появляется после вставки сообщений. Он загружает ячейку до вызова блока завершения firebase. Важно понимать, что блок кода завершения вызывается асинхронно после получения изображения firebase из URL-адреса. Итак, этот фрагмент кода является многопоточным, и вы должны заметить, когда нужно перезагрузить таблицу. - person Prcela; 05.07.2016
comment
Я уже сделал, но все еще не могу показать изображение в настоящее время. До сих пор не могу понять, почему это произошло. - person Johnny Hsieh; 05.07.2016
comment
Это зависит от того, чего вы хотите достичь. Это был бы обычный случай, когда элемент ячейки создается без изображения, и ячейка может быть обновлена ​​​​позже, когда изображение будет выбрано. - person Prcela; 05.07.2016
comment
Я просто хочу показать полный объект сообщения, и почему, когда я использую метод dispatch_async (dispatch_get_main_queue()), все равно возвращаюсь без изображений? Есть ли способ сделать так, чтобы этот полный объект отображался сразу? извините за сохранение вопроса. И очень ценю вашу доброту. - person Johnny Hsieh; 05.07.2016
comment
Вы получили какие-то данные в этой строке: myPost.userPhoto = UIImage(data: data!) ? Данные пусты или нет? - person Prcela; 05.07.2016
comment
Давайте продолжим обсуждение в чате. - person Johnny Hsieh; 05.07.2016
comment
mypost.userPhoto = UIImage(данные:данные!)? данные существуют, но когда я печатаю self.posts.description изображение == nil, кажется, что сообщение никогда не получало изображения. - person Johnny Hsieh; 07.07.2016