UICollectionView автоматический размер высоты

Как правильно изменить размер UICollectionView, чтобы он полностью отображал его содержимое? Я пробовал много вещей, включая настройку его фрейма, вызов reloadData и аннулирование макета:

self.collectionView.contentSize = CGSizeMake(300, 2000);
self.collectionView.frame = CGRectMake(0, 0, 300, 2000);
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];

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

представление коллекции, показывающее только часть содержимого после изменения размера кадра

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

РЕДАКТИРОВАТЬ: демонстрационную программу можно найти по адресу https://github.com/mjdemilliano/TestUICollectionView.

EDIT2: Я заметил, что в какой-то момент обновление кадра теряется, потому что, если я снова нажму кнопку, текущий кадр вернется к старому значению. После добавления некоторых операторов журнала в обработчик событий кнопки вывод журнала будет следующим:

before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}
before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}

Я не понимаю, почему не сохраняется смена кадра, что меняет.

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

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


person Martijn de Milliano    schedule 26.12.2013    source источник
comment
Когда вы говорите, что ваш код не действует - вы имеете в виду буквально? Установка contentSize - это правильный способ установить прокручиваемую область. Если вы не можете прокручивать, значит, вы не можете установить contentSize. Вы уверены, что self.collectionView не nil во время звонка? Может ты случайно позвонил до viewDidLoad?   -  person Tommy    schedule 27.12.2013
comment
@Tommy: Я добавил ссылку на полный тестовый проект. Код запускается после viewDidLoad, потому что он запускается при нажатии кнопки. При нажатии кнопки ничего не происходит, но операторы журнала подтверждают, что значения были обновлены. Тем не менее, я не вижу эффекта от изменения contentSize или frame представления коллекции. Обратите внимание, что я отключил прокрутку самого представления коллекции, потому что я хочу, чтобы само представление коллекции было показано полностью, которое затем должно быть заключено в представление прокрутки позже, когда это сработает.   -  person Martijn de Milliano    schedule 28.12.2013
comment
Мартейн, не могли бы вы дать мне обновленную ссылку на ваш тестовый проект? Указанная ссылка на github не работает, и я хочу сделать что-то очень похожее на то, что вы здесь описываете.   -  person John Michael Zorko    schedule 06.11.2014
comment
@JohnMichaelZorko Упс ... Я только что удалил его с Github две недели назад в спонтанном безумии очистки, не зная, что я упомянул об этом из этого вопроса. Извините! Я воссоздал его из того, что, как мне кажется, было моим локальным клоном этого тестового проекта, надеюсь, он вам поможет!   -  person Martijn de Milliano    schedule 07.11.2014
comment
Отправил ответ на аналогичный вопрос: stackoverflow.com/a/67055114/855680   -  person MLQ    schedule 12.04.2021
comment
Отвечает ли это на ваш вопрос? Как настроить высоту UICollectionView на высоту размера содержимого UICollectionView?   -  person MLQ    schedule 12.04.2021


Ответы (9)


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

self.verticalLayoutConstraint.constant = self.collectionView.contentSize.height;

Затем размер представления коллекции изменяется правильно, и он ведет себя хорошо в общем просмотре прокрутки. Я обновил тестовый проект GitHub своими изменениями.

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

person Martijn de Milliano    schedule 29.12.2013
comment
Это работает, только если вы указали фиксированную ширину в представлении коллекции (через ограничение). Если используются ограничения динамической ширины, вы должны запустить эту строку после того, как UICollectionView узнает, какой ширины она должна быть. - person Albert Bori; 15.01.2015
comment
Не могу поверить, что нет более простого способа, но я прибегал к тому же самому. - person willem; 11.03.2015
comment
Спасибо за ваш код на github, но это небольшая ошибка. После перезагрузки данных вы должны установить self.verticalLayoutConstraint.constant = self.collectionView.collectionViewLayout.collectionViewContentSize.height; вместо self.verticalLayoutConstraint.constant = self.collectionView.contentSize.height; - person pawel_d; 10.04.2015
comment
мне не кажется правильным то же самое для меня. Но работает, молодец! - person lewis; 06.10.2015
comment
collectionView.collectionViewLayout.collectionViewConte‌​ntSize.height должно быть collectionView.collectionViewLayout.collectionViewConte‌​ntSize().height ;-) - person SoftDesigner; 11.10.2016
comment
Как вы определяете, когда завершилась перезагрузка представления коллекции? - person Fergal Rooney; 09.01.2017
comment
Я сделал это внутри collectionView:willDisplayCell:forItemAtIndexPath:, и это сработало - person iosdude; 05.06.2017

Кажется, он отлично работает с настраиваемым классом UICollectionView.

class AutoSizedCollectionView: UICollectionView {

    override var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

Установите свой собственный класс в построителе интерфейса:

введите описание изображения здесь

Таким образом, вы также можете установить внутренний размер вашей коллекции как «заполнитель» в построителе интерфейса, чтобы избежать необходимости устанавливать ограничение по высоте.

введите описание изображения здесь

Надеюсь, это поможет кому-то другому.

person Trevor    schedule 19.02.2019
comment
Работал как шарм! - Swift 5 - person Steve B; 29.07.2020
comment
@SteveB нужны ли здесь какие-либо ограничения для просмотра коллекции - person iMinion; 15.08.2020
comment
Пока вы закрепляете коллекцию по бокам, сверху и снизу, вам не нужно никаких ограничений по высоте. - person Trevor; 21.08.2020

Вот моя реализация в Swift 3:

override func sizeThatFits(_ size: CGSize) -> CGSize {
    if (self.superview != nil) {
        self.superview?.layoutIfNeeded()
    }

    return collectionView.contentSize
}
person Craig Holliday    schedule 07.05.2017
comment
Спасибо!! и я думаю, что вместо этого он должен вернуть collectionView.collectionViewLayout.collectionViewContentSize - person brahimm; 01.02.2019

UICollectionViewFlowLayout *flowLayout;
flowLayout = [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:NO];
[flowLayout setItemSize:CGSizeMake(322.0, 148.0)];  //important to leave no white space between the images
[self.collectionView setCollectionViewLayout:flowLayout];

Я обнаружил, что автоматическая раскладка в раскадровке не очень помогает. Правильная настройка UICollectionViewFlowLayout для вашего collectionView - настоящая помощь. Если вы отрегулируете размер элемента с помощью setItemSize, вы можете получить желаемый результат.

person flame3    schedule 03.02.2015
comment
Для меня это не имело значения - person lewis; 06.10.2015
comment
@Pushp, можно в viewDidLoad () - person flame3; 21.11.2018

Вот способ привязать высоту CollectionView через его внутренний размер. Я использовал его для правильного определения размера CollectionView внутри ячейки TableView (с динамической высотой ячеек). и он отлично работает.

Сначала добавьте это в свой подкласс UICollectionView:

override var intrinsicContentSize: CGSize {
    get {
        return self.contentSize
    }
}

Затем вызовите layoutIfNeeded () после перезагрузки данных:

reloadData()
layoutIfNeeded()
person AmitP    schedule 11.09.2017
comment
не возражаете опубликовать полный код в Интернете? Я пробовал ваш метод, но возвращение contentSize в intrinsicContentSize фактически приводит к сворачиванию ячейки таблицы, работает только collectionViewLayout.collectionViewContentSize. Но я не могу заставить таблицу увеличивать высоту ячейки, чтобы она содержала всю коллекцию с reloadData();layoutIfNeeded() - person hgl; 09.10.2017

Самый простой метод, который я нашел, - это переопределить sizeThatFits: методы как есть:

- (CGSize)sizeThatFits:(CGSize)size
{
    if( self.superview )
        [self.superview layoutIfNeeded]; // to force evaluate the real layout

    return self.collectionViewLayout.collectionViewContentSize;
}
person Nicolas Buquet    schedule 26.01.2017

  1. Добавить IBOutlet для ограничения высоты CollectionView - ›Как @IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
  2. Добавьте ниже фрагмент кода.

введите описание изображения здесь

person thevikasnayak    schedule 19.11.2020

Вы можете попробовать мой собственный AGCollectionView класс

  • Назначьте ограничение по высоте для collectionView с помощью раскадровки или программно.

- Назначьте этот класс своему UICollectionView.

class AGCollectionView: UICollectionView {

    fileprivate var heightConstraint: NSLayoutConstraint!

    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        self.associateConstraints()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.associateConstraints()
    }

    override open func layoutSubviews() {
        super.layoutSubviews()

        if self.heightConstraint != nil {
            self.heightConstraint.constant = floor(self.contentSize.height)
        }
        else{
            self.sizeToFit()
            print("Set a heightConstraint set size to fit content")
        }
    }

    func associateConstraints() {
        // iterate through height constraints and identify

        for constraint: NSLayoutConstraint in constraints {
            if constraint.firstAttribute == .height {
                if constraint.relation == .equal {
                    heightConstraint = constraint
                }
            }
        }
    }
}
person AshvinGudaliya    schedule 31.03.2018

Для меня это даже проще, я думаю

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{
//add following code line after adding cells, before Return
...........
.........
scrollView.contentSize = = collectionView.contentSize;

//now scrollView size is equal to collectionView size. No matter how small or big it is.

return cell;
}
person user2511630    schedule 29.10.2014