UICollectionViewCells инициализируется в ScrollView с постоянным ограничением высоты - PullRequest
0 голосов
/ 30 мая 2018

В моем ViewController у меня есть UIScrollView с некоторыми элементами сверху, а под ними у меня есть UICollectionView с 2 предметами на строку.Нижнее ограничение UICollectionView связано с нижней частью представления прокрутки.Поэтому я хочу иметь одну полосу прокрутки, и в этом случае я вычисляю высоту UICollectionView, например.если у меня 11 элементов, высота (мое ограничение высоты UICollectionView) должна составлять 6 высот элементов + расстояние.Это работает отлично.Но меня беспокоит то, что я загружаю изображения из своей локальной базы данных Realm, я хочу выполнять ленивую загрузку, но повторное использование ячеек не происходит, поскольку в первый раз все они инициализируются и заполняются данными, несмотря на прокрутку вверх ивниз, они не используются повторно, они всегда в памяти.Поэтому иногда, когда я загружаю более 50 из них, я получаю предупреждения о памяти, а иногда приложение вылетает.Я надеюсь, что из-за подхода с высотой UICollectionView я использовал 1 глобальную полосу прокрутки.

Таким образом, меня беспокоит, возможно ли иметь эту функцию с полосой прокрутки и ограничением постоянной высоты, но элементы / ячейки, которые следует использовать повторно?

Ответы [ 3 ]

0 голосов
/ 26 июня 2018

Проблема

Что происходит? Вы указали высоту UICollectionview = размер контента.следовательно, он загружает каждую ячейку в память и никогда не будет повторно использовать вашу ячейку, поэтому ваша память продолжает увеличиваться.

Решения

У вас есть несколько способов исправить это.

1) удалите внешний вид прокрутки и добавьте все эти элементы в заголовок представления коллекции

2) загрузите только уменьшенное изображение в CollectionView

3) Рекомендуется: Вы указали высотуУ collectionview есть размер контента.вместо этого.Вы должны указать высоту экрана + высоту верхней части.т.е. если ваш верхний элемент имеет высоту 150, так что вы можете добавить 667 (6сек) высоты экрана к просмотру коллекции 667 + 150 = 817 (это будет размер содержимого внешнего прокрутки).
при таком подходе ваше коллекционное представление теперь может повторно использовать ячейку, если вы загрузите в нее миниатюру, это будет бонусом.

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

Надеюсь, что это полезно

0 голосов
/ 28 июня 2018

UICollectionView (и UITableView) оптимизирует память, создавая пул ячеек, которые используются повторно.Он извлекает количество ячеек, которое необходимо отобразить, из пула с учетом изменения положения прокрутки и / или доступного пространства (путем изменения его размера).Под «показом» я не обязательно подразумеваю видимое.В вашем случае collectionView может не отображаться, если он встроен в UIScrollView и размещен ниже границ экрана.Ячейка отображается, когда collectionView.bounds содержит «cell.frame» (даже частично).Я процитировал «cell.frame», потому что речь идет не о экземпляре ячейки, а о внутреннем механизме UICollectionView для отслеживания фрейма каждой ячейки.

Что вы делаете, так это предоставляете вашему collectionView достаточно места(регулируя его высоту), чтобы отобразить весь контент, который вы хотите отобразить.Так что никакой оптимизации там не делается.

Я могу представить три варианта:

1) Вместо встраивания UICollectionView в UIScrollView лучше использовать только один UICollectionView.Затем сделайте ваш контент с точки зрения ячеек.Это содержание выше вашего collectionView?Сделайте это клеткой (или связкой ячеек).Заставьте его жить в своем собственном разделе со своими вставками и прочим.При реализации этой опции я обычно определяю перечисление с регистрами, являющимися необходимыми типами контента.

enum ContentType {
    case header, cell(Any)
}

var data: [ContentType] = [...]

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    switch data[indexPath.row] {
        case .header:
            return headerDataSource.cell // Some object you can obtain the cell from
        case .cell(value):
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
            cell.fill(value)
            return cell
    }
}

2) Этот параметр является личным предпочтением с точки зрения дизайна.Я предлагаю вам установить ограничение на количество ячеек, которые отображает ваш collectionView, отключить прокрутку collectionView, установить высоту collectionView равным contentSize.height и добавить ячейку «More», которая отправляет пользователя в UICollectionViewController, который отображает остальную часть содержимого, и использовать преимуществаоптимизации.Так как мы ограничиваем количество отображаемых ячеек, это не повлияет на память, если ограничение не установлено слишком высоким (очевидно).Я реализовал эту опцию один раз, когда мне нужно было отобразить галерею с некоторым содержимым над ней, с кучей других видов ячеек под ней и некоторыми записями обзора внизу (тоже с ячейками), и я не хотел, чтобы пользователь прокручивал,скажем, 1000 галерей, чтобы добраться до отзывов.Также не хотелось, чтобы пользователь снова прокрутил эти 1000 ячеек, чтобы добраться до контента над галереей.Я установил ограничение в 8 ячеек галереи плюс ячейка «Еще», чтобы создать сетку 3х3, которая выглядит красиво и просто.

3) Я думаю, что эта излишняя.Не стоит, когда вы получили вариант 1. Вы можете реализовать scrollViewDidScroll :, рассчитать фрейм встроенного collectionView (в терминах системы координат представления контроллера представления), используя convert (_: to :) и отрегулировать ограничение по высоте для collectionView, чтобы оно было дельтой scrollView.высота фрейма минус collectionView ранее вычисленного minY фрейма и гарантирует, что высота collectionView не превышает высоту фрейма scrollView.Когда это происходит, нужно добавить дополнительную дельту к границам collectionView, чтобы прокрутить ее программно.Есть еще много соображений, которые я не смог решить, и у меня не было никакого интереса сделать это, учитывая, что я могу реализовать вариант 1.

// This code is not working in an acceptable way but I think is a reasonable starting point.
// Also, when bouncing is disabled, a scrollView doesn't call this method when trying to scroll past the edges.
extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard !(scrollView is UICollectionView) else {return}
        let height = view.bounds.height
        let minY = collectionView.convert(collectionView.bounds, to: view).minY
        let delta = height - minY
        constraint_collection_height.constant = delta
        if delta > height {
            constraint_collection_height.constant = height
            let offset = collectionView.convert(CGPoint.zero, to: scrollView).y
            let scroll = scrollView.bounds.minY - offset
            let bounds = CGRect(origin: CGPoint(x: 0, y: scroll), size: collectionView.bounds.size)
            collectionView.bounds = bounds
        }
    }
}

Редактировать: - Пример ответа на комментарий.

Вместо встраивания collectionView в ячейку для управления другим типом макета.Нужно управлять новым разделом и определить новый макет там.

enum ContentType {
    case header, cell(Any), image(UIImage)
}

var data_section0: [ContentType] = [...]
var data_section1: [ContentType] = [...]

func retrieve_cell(_ collectionView: UICollectionView, _ data: [ContentType], _ row: Int) -> UICollectionViewCell {
    switch data[row] {
        case .header:
            return headerDataSource.cell // Some object you can obtain the cell from
        case .cell(value):
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
            cell.fill(value)
            return cell
        case .image(let image):
            let cell = ...
    }
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    switch indexPath.section {
        case 0: return retrieve_cell(collectionView, data_section0, indexPath.row)
        case 1: return retrieve_cell(collectionView, data_section1, indexPath.row)
        default: fatalError("There' shouldn't be a 3rd section")
    }
}

func numberOfSections(in collectionView: UICollectionView) -> Int {return 2}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    switch section {
        case 0: return data_section0.count
        case 1: return data_section1.count
        default: fatalError("There' shouldn't be a 3rd section")
    }
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    switch indexPath.section {
        case 0: return collectionView.bounds.width
        case 1: return collectionView.bounds.width / 2
        default: fatalError("There' shouldn't be a 3rd section")
    }
}
0 голосов
/ 23 июня 2018

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

В зависимости от содержимого ваших ячеек, вы можете попытаться запросить видимые пути индекса UICollectionViewLayout, используя layoutAttributesForElementsInRect:, и загрузить объемное содержимое для этих ячеек, как только ваш внешний скролл прокручивает их в поле зрения.Для этого вы можете установить свойство делегата вашего внешнего scrollview и получать обновления через scrollViewDidScroll.Конечно, вам также придется беспокоиться о выгрузке изображений после их прокрутки за пределами экрана.

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