Як визначити висоту UICollectionView за допомогою FlowLayout


118

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

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

Оскільки я не зміг знайти жодних натяків на цю тему в офіційній документації, і Google не приніс мені більше, будь-яка допомога та ідеї будуть дуже вдячні.

Відповіді:


254

Ого! Чомусь після годинних досліджень я знайшов досить просту відповідь на моє запитання: я повністю шукав у невірному місці, перекопуючи всю документацію, на яку я міг знайти UICollectionView.

Просте і просте рішення полягає в макеті, що лежить в основі: Просто зателефонуйте collectionViewContentSizeна вашу myCollectionView.collectionViewLayoutвласність, і ви отримаєте як висоту, так і ширину вмісту як CGSize. Це так просто, як це.


1
хтось, я хочу, щоб у UITableView було щось подібне
Chris Chen,

1
Ігнатус, @jbandi, Chris та ін., Чи можете ви розширити це? Коли я перевіряв горизонтальний напрямок прокрутки з великим міжрядковим інтервалом (щоб елементи виходили горизонтально), подання колекції повернуло недійсний внутрішній розмір вмісту (-1, -1), а collectionViewContentSize повернув те, що було ефективно межі подання колекції - не беручи до уваги те, що в просторі був оточений простір. Словом, це було не дуже суттєво. Дякую!
Кріс Коновер

8
Яка різниця між collectionViewContentSize та contentSize подання прокрутки?
рунак

Коли ви викликаєте contentSize перегляду колекції в viewDidLoad, він повертає кадр подання колекції, collectionViewContentSize повертає правильний розмір вмісту.
Fury

1
Для кого це може допомогти: я повинен був зробити "layoutIfNeeded ()" перед цим, щоб я міг змусити його працювати.
asanli

37

Якщо ви використовуєте автоматичний макет , ви можете створити підклас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

12
не працювало для мене ... Я використовую мій CollectionView всередині UITableViewCell і хотів би, щоб перегляд таблиці збільшувався у висоту, коли вигляд колекції зростає у висоту за допомогою iOS8 UITableViewAutomaticDimension Але мій перегляд колекції залишається малим :(
Георг

Дивовижно. Коротке і солодке рішення мого UICollectionView всередині UIScrollView!
dotToString

2
Одне важливе - відключити прокрутку та смуги прокрутки у вікні колекції
Георг

1
Та ж проблема, що і Георг, це близько 50 пікс у висоту, коли вона повинна бути більше 400
xtrinch

3
Так, я встановив перегляд колекції на "не скроллаб, не скольбар", а потім перезавантажую його, після чого встановлюється вміст перегляду таблиці. Також колекція перегляду є підкласом, який повертає його розмір вмісту як intrinsicContentSize. Сподіваюся, що це допомагає!
Георг

25

У 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];

11

відповідь user1046037 у Swift ...

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

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

Це ідеально, але якщо у вас є щось на кшталт UILabelвище, вміст із collectionViewпереходу на інший елемент вгорі, чи є у вас інше рішення?
KolaCaine

11

Код Swift 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
    }

}

Я свіжіший, і я не впевнений, де і як це використовувати в моєму ViewController. Чи можете ви, будь ласка, змінити відповідь .....?
Абірамі Бала

1
просто встановіть цей клас у вашій колекції перегляду в розділі "Розповідь"
Мохаммед Садік Шейх

7

Швидкий 4

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

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

1
Я виявив, що його не працює в iPhone 6/7 плюс та XR, XS-MAX
Rahul K Rajan

7

Швидкий 4.2

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

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

4

На це запитання відповісти пізно, але я нещодавно пережив це питання. Вищеназвана відповідь
@ lee дуже допомагає мені отримати відповідь. Але ця відповідь обмежена ціллю-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")
}

Сподіваюся, це допоможе вам швидко вирішити свою проблему.


@ShikhaSharma: Ви можете додати код RemoveObserver в рамках методу "ViewDidDisappear", який видалить спостерігача лише після того, як представлення підтвердить, що його зникло. Перевірте оновлену відповідь.
Ер. Vihar

я отримую цю помилку: -observeValueForKeyPath: ofObject: change: context: повідомлення отримано, але не обробляється.
Шиха Шарма

@ShikhaSharma: Вибачте, моя помилка вказати метод. Спробуйте помістити код Removeobserver в viewWillDisappear.
Ер. Віхар

1

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

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

0

Швидка версія @ 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
    }
}

0

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)
}

До цього

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

Після

обмеження висоти щодо безпечної зони хороший результат тренажера


0

припускаючи, що ваш collectionView названий collectionView, ви можете взяти висоту наступним чином.

let height = collectionView.collectionViewLayout.collectionViewContentSize.height

0

Крім того, вам краще зателефонувати, reloadDataперш ніж отримати висоту:

theCollectionView.collectionViewLayout.collectionViewContentSize;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.