Как определить высоту UICollectionView с FlowLayout

У меня есть UICollectionView с UICollectionViewFlowLayout, и я хочу рассчитать его размер содержимого (для возврата в intrinsicContentSize, необходимого для настройки его высоты с помощью AutoLayout).

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

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

Ответ 1

Вау! По некоторым причинам, после нескольких часов исследований, я нашел довольно простой ответ на мой вопрос: я полностью искал не то место, прорывая всю документацию, которую я мог найти на UICollectionView.

Простое и простое решение лежит в базовом макете: просто назовите collectionViewContentSize своим свойством myCollectionView.collectionViewLayout, и вы получите высоту и ширину содержимого как CGSize. Это так просто.

Ответ 2

Если вы используете Автомакет, вы можете создать подкласс UICollectionView

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

Ниже приведена реализация:

@interface DynamicCollectionView : UICollectionView

@end

@implementation DynamicCollectionView

- (void) layoutSubviews
{
    [super layoutSubviews];

    if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize]))
    {
        [self invalidateIntrinsicContentSize];
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize intrinsicContentSize = self.contentSize;

    return intrinsicContentSize;
}

@end

Ответ 3

В viewDidAppear вы можете получить его:

float height = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;

Возможно, когда вы перезагружаете данные, вам нужно вычислить новую высоту с новыми данными, тогда вы можете получить ее: добавьте наблюдателя для прослушивания, когда ваши CollectionView закончили перезагружать данные в viewdidload:

[self.myCollectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

Затем добавьте следующую функцию, чтобы получить новую высоту или сделать что-либо после завершения загрузки коллекции:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    //Whatever you do here when the reloadData finished
    float newHeight = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;    
}

И не забудьте удалить наблюдателя:

[self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];

Ответ 4

user1046037 ответ в Swift...

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize() {
            invalidateIntrinsicContentSize()
        }
    }

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}

Ответ 5

Свифт 3 для пользователя1046037 ответ

import UIKit

class DynamicCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
            self.invalidateIntrinsicContentSize()
        }

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

Ответ 6

Swift 4

class DynamicCollectionView: UICollectionView {
  override func layoutSubviews() {
    super.layoutSubviews()
    if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
      self.invalidateIntrinsicContentSize()
    }
  }

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}

Ответ 7

Слишком поздно, чтобы ответить на этот вопрос, но я недавно пережил этот вопрос.
@lee выше ответ помогите мне многое, чтобы получить мой ответ. Но этот ответ ограничен objective-c, и я работал в быстро.
@lee Что касается вашего ответа, позвольте мне позволить быстрому пользователю решить эту проблему легко. Для этого выполните следующие шаги:

Объявить переменную CGFloat в разделе объявления:

var height : CGFloat!

В viewDidAppear вы можете получить его:

height = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

Может быть, когда вы reload данных, тогда вам нужно вычислить новую высоту с новыми данными, тогда вы можете получить ее: addObserver для прослушивания, когда ваши CollectionView закончили перезагружать данные в viewWillAppear:

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)
        ....
        ....
        self.shapeCollectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old, context: nil)
    }

Затем добавьте следующую функцию, чтобы получить новую высоту или сделать что-либо после CollectionView завершена перезагрузка:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

        var frame : CGRect! = self.myCollectionView.frame
        frame.size.height = newHeight

        self.myCollectionView.frame = frame
    }

И не забудьте удалить наблюдателя:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)
    ....
    ....
    self.myCollectionView.removeObserver(self, forKeyPath: "contentSize")
}

Я надеюсь, что это поможет вам быстро решить вашу проблему.

Ответ 8

Swift 4.2

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize {
            invalidateIntrinsicContentSize()
        }
    }

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

Ответ 9

Swift версия @user1046037:

public class DynamicCollectionView: UICollectionView {

    override public func layoutSubviews() {
        super.layoutSubviews()
        if !bounds.size.equalTo(intrinsicContentSize) {
            invalidateIntrinsicContentSize()
        }
    }

    override public var intrinsicContentSize: CGSize {
        let intrinsicContentSize: CGSize = contentSize
        return intrinsicContentSize
    }
}

Ответ 10

Кроме того: перед вызовом reloadData лучше вызвать reloadData: myCollectionView.collectionViewLayout.collectionViewContentSize

Ответ 11

Swift 4.2, Xcode 10.1, iOS 12.1:

По какой-то причине collectionView.contentSize.height выглядел меньше разрешенной высоты моего представления коллекции. Во-первых, я использовал ограничение авторазметки относительно 1/2 высоты суперпредставления. Чтобы исправить это, я изменил ограничение на "безопасную область" представления.

Это позволило мне установить высоту ячейки для заполнения представления моей коллекции, используя collectionView.contentSize.height:

private func setCellSize() {
    let height: CGFloat = (collectionView.contentSize.height) / CGFloat(numberOfRows)
    let width: CGFloat = view.frame.width - CGFloat(horizontalCellMargin * 2)

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: width, height: height)
}

До

height constraint relative to superview bad simulator result

После

height constraint relative to safe area good simulator result

Ответ 12

Этот ответ основан на @Er. Вихар ответь. Вы можете легко внедрить решение, используя RxSwift

     collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in
        print("sizer \(size)")
    }).disposed(by: rx.disposeBag)