Jak określić wysokość UICollectionView za pomocą FlowLayout

118

Mam UICollectionViewz ikoną UICollectionViewFlowLayouti chcę obliczyć rozmiar jego zawartości (do zwrotu intrinsicContentSizepotrzebnego do dostosowania jego wysokości za pomocą AutoLayout).

Problem polega na tym, że nawet jeśli mam stałą i równą wysokość dla wszystkich komórek, nie wiem, ile "wierszy" / linii mam w pliku UICollectionView. Nie mogę również określić tej liczby na podstawie liczby elementów w moim źródle danych, ponieważ komórki reprezentujące elementy danych różnią się szerokością, podobnie jak liczba elementów, które mam w jednym wierszu pliku UICollectionView.

Ponieważ nie mogłem znaleźć żadnych wskazówek na ten temat w oficjalnej dokumentacji, a googlowanie nie przyniosło mi nic więcej, każda pomoc i pomysły byłyby bardzo mile widziane.

Ignatius Tremor
źródło

Odpowiedzi:

254

Whoa! Z jakiegoś powodu, po godzinach poszukiwań, znalazłem teraz dość łatwą odpowiedź na moje pytanie: kompletnie szukałem w złym miejscu, przekopując się przez całą dokumentację, w której mogłem znaleźć UICollectionView.

Proste i łatwe rozwiązanie leży w podstawowym układzie: po prostu wywołaj collectionViewContentSizeswoją myCollectionView.collectionViewLayoutnieruchomość, a otrzymasz wysokość i szerokość treści jako CGSize. To takie proste.

Ignatius Tremor
źródło
1
whoa, chciałbym, żeby UITableView miało coś podobnego
Chris Chen
1
Ignatus, @jbandi, Chris i wsp., Czy możesz to rozwinąć? Kiedy testowałem z poziomym kierunkiem przewijania z dużymi odstępami między elementami (tak, aby elementy układały się poziomo), widok kolekcji zwrócił nieprawidłowy wewnętrzny rozmiar zawartości (-1, -1), a collectionViewContentSize zwróciła to, co było skutecznie granice widoku kolekcji - nie biorąc pod uwagę, że był tylko jeden wiersz otoczony spacją. Krótko mówiąc, nie było to bardzo istotne. Dzięki!
Chris Conover
8
Jaka jest różnica między collectionViewContentSize i contentSize widoku przewijania?
rounak
Gdy wywołujesz contentSize widoku kolekcji w viewDidLoad, zwraca ramkę widoku kolekcji, collectionViewContentSize zwraca prawidłowy rozmiar zawartości.
Fury,
1
Komu to może pomóc: musiałem przed tym zrobić "layoutIfNeeded ()", żeby to działało.
asanli
37

W przypadku korzystania z układu automatycznego można utworzyć podklasęUICollectionView

Jeśli użyjesz poniższego kodu, nie musisz określać żadnych ograniczeń wysokości dla widoku kolekcji, ponieważ będzie się on różnić w zależności od zawartości widoku kolekcji.

Poniżej podano wykonanie:

@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
user1046037
źródło
12
nie działa dla mnie ... Używam mojego CollectionView wewnątrz UITableViewCell i chciałbym, aby widok tabeli zwiększał się wraz ze wzrostem wysokości widoku kolekcji za pomocą iOS8 UITableViewAutomaticDimension Ale mój widok kolekcji pozostaje mały :(
Georg
Niesamowite. Krótkie i przyjemne rozwiązanie dla mojego UICollectionView w UIScrollView!
dotToString
2
Ważną rzeczą jest wyłączenie przewijania i pasków przewijania w widoku kolekcji
Georg
1
Ten sam problem co Georg, ma około 50 pikseli wysokości, a powinien być większy niż 400
xtrinch
3
Tak, ustawiam widok kolekcji na „nie przewijaj, nie ma pasków przewijania”, a następnie ładuję go ponownie, a następnie ustawiana jest zawartość widoku tabeli. Również widok kolekcji jest podklasą, która zwraca swój rozmiar zawartości jako intrinsicContentSize. Mam nadzieję, że to pomoże!
Georg
25

Co viewDidAppearmożna je dostać przez:

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

Może kiedy przeładujesz dane, a następnie będziesz musiał obliczyć nową wysokość z nowymi danymi, możesz to uzyskać przez: dodaj obserwatora, aby nasłuchiwał, kiedy CollectionViewzakończysz przeładowywanie danych o viewdidload:

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

Następnie dodaj poniżej funkcję, aby uzyskać nową wysokość lub zrób cokolwiek po zakończeniu ponownego ładowania widoku kolekcji:

- (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;    
}

I nie zapomnij usunąć obserwatora:

[self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
zawietrzny
źródło
11

user1046037 odpowiedź w Swift ...

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

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}
Nick Wood
źródło
Działa idealnie, ale jeśli masz coś podobnego do UILabelpowyższego, treść z collectionViewprzejścia nad innym elementem powyżej, czy masz inne rozwiązanie?
KolaCaine
11

Kod Swift 3 dla odpowiedzi użytkownika 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
    }

}
Mohammad Sadiq Shaikh
źródło
Jestem świeższy i nie jestem pewien, gdzie i jak tego użyć w moim ViewController. Czy możesz wprowadzić zmiany z odpowiedzią .....?
Abirami Bala
1
po prostu ustaw tę klasę na swój widok kolekcji w Storyboard
Mohammad Sadiq Shaikh
7

Szybki 4

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

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}
Wilson
źródło
1
Stwierdziłem, że nie działa w iPhone 6/7 plus i XR, XS-MAX
Rahul K Rajan
7

Swift 4.2

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

    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}
Cesare
źródło
4

Jest za późno, aby odpowiedzieć na to pytanie, ale ostatnio omawiałem ten problem. Powyższa odpowiedź
@ lee bardzo mi pomogła w uzyskaniu odpowiedzi. Ale ta odpowiedź ogranicza się do celu-c i pracowałem szybko . @lee W nawiązaniu do Twojej odpowiedzi, pozwól mi pozwolić szybkiemu użytkownikowi na łatwe rozwiązanie tego problemu. W tym celu wykonaj poniższe czynności:

Zadeklaruj CGFloatzmienną w sekcji deklaracji:

var height : CGFloat!

Co viewDidAppearmożna je dostać przez:

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

Może kiedy reloaddane będą musiały obliczyć nową wysokość przy użyciu nowych danych, można to uzyskać w następujący sposób: addObserveraby nasłuchiwać, kiedy CollectionViewzakończyliśmy ponowne ładowanie danych o viewWillAppear:

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

Następnie dodaj poniżej funkcję, aby uzyskać nową wysokość lub zrób cokolwiek po collectionviewzakończeniu przeładowania:

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
    }

I nie zapomnij usunąć obserwatora:

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

Mam nadzieję, że pomoże to w szybkim rozwiązaniu problemu.

Er. Vihar
źródło
@ShikhaSharma: Możesz dodać kod removeObserver w ramach metody „ViewDidDisappear”, która usunie obserwatora dopiero po potwierdzeniu przez widok, że zniknął. Sprawdź zaktualizowaną odpowiedź.
Eee. Vihar
Otrzymuję ten błąd: An -observeValueForKeyPath: ofObject: change: context: wiadomość została odebrana, ale nie została obsłużona.
Shikha Sharma
@ShikhaSharma: Przepraszamy, określenie metody to mój błąd. Spróbuj umieścić kod removeobserver w viewWillDisappear.
Eee. Vihar
1

Ta odpowiedź jest oparta na @Er. Odpowiedź Vihara. Możesz łatwo wdrożyć rozwiązanie za pomocą RxSwift

     collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in
        print("sizer \(size)")
    }).disposed(by: rx.disposeBag)
HUNG TRAN DUY
źródło
0

Szybka wersja @ 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
    }
}
Joshua Hart
źródło
0

Swift 4.2, Xcode 10.1, iOS 12.1:

Z jakiegoś powodu collectionView.contentSize.heightwydawał się mniejszy niż ustalona wysokość widoku mojej kolekcji. Po pierwsze, użyłem ograniczenia automatycznego układu w stosunku do 1/2 wysokości nadzoru. Aby to naprawić, zmieniłem ograniczenie na „bezpieczny obszar” widoku.

Pozwoliło mi to ustawić wysokość komórki w celu wypełnienia widoku kolekcji za pomocą 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)
}

Przed

ograniczenie wysokości względem superview zły wynik symulatora

Po

ograniczenie wysokości względem obszaru bezpiecznego dobry wynik symulatora

Andrew Kirna
źródło
0

zakładając, że collectionView ma nazwę collectionView , możesz przyjąć wysokość w następujący sposób.

let height = collectionView.collectionViewLayout.collectionViewContentSize.height
Pramodya Abeysinghe
źródło
0

Ponadto , lepiej zadzwoń reloadDataprzed uzyskaniem wzrostu:

theCollectionView.collectionViewLayout.collectionViewContentSize;
guozqzzu
źródło