Обновите свой интерфейс в главном потоке.

ичок в разработке для iOS и хочу создать приложение-клон instagram, и у меня возникла проблема при создании новостной ленты для приложения-клона instagram.

Поэтому я использую Firebase для хранения изображения и базы данных. после публикации изображения (загрузки данных в Firebase) я хочу заполнить табличное представление, используя загруженные данные из моей базы.

Но когда я запускаю приложение, фиктивное изображение и метка из моей раскадровки перекрывают загруженные данные, которые я помещаю в табличное представление. данные, которые я загружаю, в конечном итоге будут отображаться после прокрутки вниз.

Вот gif, когда я запускаю приложение:

http://g.recordit.co/iGIybD9Pur.gif

Есть 3 пользователей, которые показывают в .gif

имя пользователя (манекен из раскадровки)JokowiRIMegawatiRI

После асинхронной загрузки изображения из Firebase (после закрытия индикатора загрузки) я ожидаю, что MegawatiRI будет отображаться в верхней части таблицы, но сначала появится пустышка, но после прокрутки вниз и возврата к началу MegawatiRI в конечном итоге появляется.

Я считаю, что MegawatiRI успешно загружен, но я не знаю, почему фиктивное изображение кажется перекрывающим фактические данные. Я не хочу, чтобы манекен показывался при запуске моего приложения.

Вот скриншот ячейки прототипа:

А вот упрощенные коды контроллера табличного представления:

class NewsFeedTableViewController: UITableViewController {


    var currentUser : User!
    var media = [Media]()



    override func viewDidLoad() {
        super.viewDidLoad()



        tabBarController?.delegate = self


        // to set the dynamic height of table view
        tableView.estimatedRowHeight = StoryBoard.mediaCellDefaultHeight
        tableView.rowHeight = UITableViewAutomaticDimension

        // to erase the separator in the table view
        tableView.separatorColor = UIColor.clear

    }


    override func viewWillAppear(_ animated: Bool) {

        // check wheter the user has already logged in or not

        Auth.auth().addStateDidChangeListener { (auth, user) in

            if let user = user {

                RealTimeDatabaseReference.users(uid: user.uid).reference().observeSingleEvent(of: .value, with: { (snapshot) in

                    if let userDict = snapshot.value as? [String:Any] {
                        self.currentUser = User(dictionary: userDict)
                    }


                })

            } else {
                // user not logged in
                self.performSegue(withIdentifier: StoryBoard.showWelcomeScreen, sender: nil)
            }


        }

        tableView.reloadData()
        fetchMedia()



    }




    func fetchMedia() {

    SVProgressHUD.show()

    Media.observeNewMedia { (mediaData) in


        if !self.media.contains(mediaData) {
            self.media.insert(mediaData, at: 0)
            self.tableView.reloadData()
            SVProgressHUD.dismiss()
        }


        }

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: StoryBoard.mediaCell, for: indexPath) as! MediaTableViewCell


        cell.currentUser = currentUser
        cell.media = media[indexPath.section]



        // to remove table view highlight style
        cell.selectionStyle = .none



        return cell
    }


    }

А вот упрощенный код ячейки табличного представления:

class MediaTableViewCell: UITableViewCell {

    var currentUser: User!
    var media: Media! {
        didSet {

            if currentUser != nil {
                updateUI()
            }

        }
    }

    var cache = SAMCache.shared()



    func updateUI () {


        // check, if the image has already been downloaded and cached then just used the image, otherwise download from firebase storage


        self.mediaImageView.image = nil
        let cacheKey = "\(self.media.mediaUID))-postImage"

        if let image = cache?.object(forKey: cacheKey) as? UIImage {
            mediaImageView.image = image
        } else {
            media.downloadMediaImage { [weak self] (image, error) in

                if error != nil {
                    print(error!)
                }


                if let image = image {
                    self?.mediaImageView.image = image
                    self?.cache?.setObject(image, forKey: cacheKey)

                }


            }

        }

Так что же делает фиктивное изображение перекрывающим мои загруженные данные?

Ответы на вопрос(2)

Решение Вопроса
Ответ

потому что ваш контроллер табличного представления начинает рендеринг ячеек, прежде чем ваш текущий пользователь будет правильно установлен на tableViewController

Таким образом, при первом вызове cellForRowAtIndexPath у вас, вероятно, в контроллере nil currentUser, который передается в ячейку. Следовательно, наблюдатель свойства didSet в вашем классе ячеек не вызывает updateUI ():

didSet {
        if currentUser != nil {
            updateUI()
        }
    }

Позже вы перезагружаете данные, и текущий пользователь уже настроен, поэтому все начинает работать, как и ожидалось.

Эта строка из вашего updateUI () должна скрывать ваше фиктивное изображение. Однако updateUI не всегда вызывается, как описано выше:

self.mediaImageView.image = nil

Я на самом деле не вижу причины, по которой updateUI требует, чтобы текущий пользователь был не ноль Таким образом, вы можете просто удалить тест nil в обозревателе didSet и всегда вызывать updateUI:

var media: Media! {
    didSet {
        updateUI()
    }

В качестве альтернативы вы могли бы переставить контроллер табличного представления так, чтобы он фактически ожидал установки текущего пользователя перед загрузкой источника данных. Связанный с логином код в вашем viewWillAppear имеет вложенные обработчики завершения для установки текущего пользователя. Скорее всего, они выполняются асинхронно ... поэтому вам нужно либо дождаться их завершения, либо иметь дело с текущим пользователем, равным нулю.

Auth.auth etc { 
   // completes asynchronously, setting currentUser 
}
// Unless you do something to wait, the rest starts IMMEDIATELY
// currentUser is not set yet

tableView.reloadData()
fetchMedia()
Другие заметки

(1) Я думаю, что было бы неплохо перезагрузить ячейку (используя reloadRows), когда изображение загрузится и будет вставлено в общий кэш. Вы можете обратиться к ответам в этомвопрос чтобы увидеть, как асинхронная задача, инициированная из ячейки, может связаться с tableViewController, используя NotificationCenter или делегирование.

(2) Я подозреваю, что ваши задачи загрузки изображений в настоящее время выполняются в основном потоке, что, вероятно, не то, что вы хотели. Когда вы исправите это, выбудем необходимо переключиться обратно в основной поток, чтобы либо обновить изображение (как вы делаете сейчас), либо перезагрузить строку (как я рекомендую выше).

 Agung Laksana27 окт. 2017 г., 04:22
спасибо Оззи, моя проблема была решена. теперь я полностью понимаю :)

Обновите свой интерфейс в главном потоке.

if let image = image {
DispatchQueue.main.async {
    self?.mediaImageView.image = image
    }
self?.cache?.setObject(image, forKey: cacheKey)
}

Ваш ответ на вопрос