Загружать ячейки CollectionView только тогда, когда все данные загружены - PullRequest
0 голосов
/ 30 сентября 2018

Я заполняю свой CollectionView функцией loadData (), которая вызывается в методе ViewDidLoad ().Здесь я анализирую все данные из моей базы данных Firebase в реальном времени в массив (сообщения).Затем в методе cellForItemAt я установил все изображения, метки и текстовые представления соответственно на основе информации в массиве сообщений, используя indexPath.item.Довольно простые вещи.

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

Проблема, с которой я сталкивался ранее: Я загрузил данные из записей в loadData () и затем получил бы информацию о пользователе в методе cellForItemAt на основе идентификатора пользователя, сохраненного в массиве записей.Это вызвало нестабильность моего приложения: прокрутка к новым ячейкам инициировала метод cellForItemAt, заставляя его запрашивать данные, а затем обновлять их.Так что будет задержка, так как информация должна была быть загружена.Абсолютно уверен, что это было причиной, так как теперь я установил для него изображение по умолчанию (без изображения в профиле) и имя пользователя по умолчанию («Имя пользователя»), что делает его снова очень плавным.

Затем я перешел к извлечениюuserData и проанализируйте его в другом массиве (userInfo):

struct userData {
    var userFirstName: String
    var userLastName: String
    var userProfilePicURL: String
}

var userInfo = [String : userData]()

Я могу использовать это как userInfo [posts.userID], что именно то, что я искал.Проблема, с которой я столкнулся, состоит в том, что userInfo не заполняется во времени, возвращая ноль, когда я выгружаю массив в cellForItemAt:

dump(userInfo[post.userID])

Так что это возвращает ноль при загрузке приложения, но когда я прокручиваю, и таким образомИнициализируйте cellForItemAt снова, он возвращает значения.Таким образом, мое обоснованное предположение состоит в том, что данные не извлекаются вовремя.Сейчас я ищу способ вызывать cellForItemAt только тогда, когда загружается массив posts и пользовательский массив.

Как добавить значения в пользовательский массив, внутри функции loadData, где dict ["userID"]полученные путем наблюдения за сообщениями в базе данных:

Ref.child("users").child(dict["userID"] as! String).observeSingleEvent(of: .value) { (snapshot) in

    let userValues = snapshot.value as! [String: Any]
    userInfo[dict["userID"] as! String] = userData(userFirstName: userValues["userFirstName"] as! String, userLastName: userValues["userLastName"] as! String, userProfilePicURL: userValues["userProfilePicURL"] as! String)

}

Я хочу убедиться, что информация добавляется в массив, прежде чем показывать ячейки, чтобы они могли соответственно изменить изображение профиля и имя пользователя.Я хочу сделать это в методе cellForItemAt.Я думал об использовании таймеров, скрывая свой CollectionView на пару секунд, но все это будет зависеть от скорости соединения и т. Д., Поэтому я думаю, что должно быть более подходящее решение.

Любые полезные идеи приветствуются!

Ответы [ 4 ]

0 голосов
/ 01 октября 2018

Что бы я сделал в сценарии такого типа,

  1. Не устанавливайте делегировать представления коллекции на ваш контроллер.
  2. Выполните запрос Firebase 1загрузить данные в ваш массив1. Внутри завершения первого запроса, вызовите другую функцию, которая выполняет request2 для извлечения и загрузки данных в массив 2.
  3. Внутри обработчика завершения 2-го запроса установите делегата и перезагрузите данные (в основном потоке)

Если вы не хотите вложенных вызовов.Вы можете запустить оба запроса параллельно и ждать завершения вызовов. Затем установите делегат и перезагрузите данные

См. этот Полезный ответ для получения подробной информации о том, как это сделать.

Надеждаэто помогает

0 голосов
/ 30 сентября 2018

Вы спрашивали много вещей.Да, это правда, что данные могут не быть загружены к моменту отображения представления.К счастью, UICollectionView был разработан с учетом этого.Я рекомендую посмотреть канал YouTube "Let's Build That App", чтобы получить полезные советы по использованию UICollectionView.

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

cellForItemAt не время для вызова сервера.Используйте это после загрузки данных.В общем, не смешивайте загрузку вашего сервера с отображением данных.Напишите UICollectionView с намерением, чтобы он работал над внутренним списком элементов.Это также сделает ваш код более читабельным.

Добавьте свои медленные серверные функции таким образом, чтобы они быстро обновляли данные и вызывали reloadData () после того, как вы это сделали.Я приведу код ниже.

В нашем UICollectionViewController мы сохраняем массив текущего содержимого, что-то вроде этого.Мы начинаем с этих переменных вверху:

var tasks = [URLSessionDataTask]()
var _entries = [DirectoryEntry]()
var entries: [DirectoryEntry] {
    get {
        return self._entries
    }
    set(newEntries) {
        DispatchQueue.main.async {
            self._entries = newEntries
        }
    }
}

Каждый раз, когда мы выбираем данные с сервера, мы делаем что-то вроде:

func loadDataFromServer() {

    // Cancel all existing server requests
    for task in tasks {
        task.cancel()
    }
    tasks = [URLSessionDataTask]()

    // Setup your server session and connection
    // { Your Code Here }

    let task = URLSession.shared.dataTask(with: subscriptionsURL!.url!, completionHandler: { data, response, error in
        if let error = error {
            // Process errors
            return
        }

        guard let httpresponse = response as? HTTPURLResponse,
            (200...299).contains(httpresponse.statusCode) else {
                //print("Problem in response code");
                // We need to deal with this.
                return
        }
        // Collect your data and response from the server here populate apiResonse.items
        // {Your Code Here}
        var entries = [DirectoryEntry]()

        for dataitem in apiResponse.items {
            let entry = Entry(dataitem)
            entries.append(dataitem)
        }
        self.entries = entries
        self.reloadData()
    })
    task.resume()
    tasks.append(task)
}

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

Во-вторых, мы загружаем данные в локальный массив в функции загрузки сервера (loadDataFromServer), чтобы мы не изменяли текущую версию данных, поскольку мы все еще загружаем их.В противном случае вы получите ошибки, потому что количество элементов и содержимого может измениться во время фактического отображения данных, и iOS это не понравится и будет зависать.Когда данные полностью загружены, мы загружаем их в UIViewController.Мы сделали это более элегантно с помощью установщика.

self.entries = entries

В-третьих, вы должны указать UICollectionView, что вы изменили свои данные, поэтому вы должны вызвать reloadData ().

self.reloadData()

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

Как и когда вы решите загрузить данные с сервера, доВы и ваш дизайн пользовательского интерфейса.Вы можете сделать свой первый вызов в viewDidLoad ().

0 голосов
/ 30 сентября 2018

Этого можно добиться с помощью раскадровки, которая вам не нужна, чтобы присоединиться к делегату и источнику данных в представлении коллекции.И в классе контроллера, когда вы получаете данные, затем просто установите collectionview.delegate = self и datasource и перезагрузите представление этой коллекции.

0 голосов
/ 30 сентября 2018

Вы можете использовать механизм Prefetching, который Apple представила в IOS 10.Они объяснили это в примере по следующей ссылке.

https://developer.apple.com/documentation/uikit/uicollectionviewdatasourceprefetching/prefetching_collection_view_data

Надеюсь, это решит вашу проблему.

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