Zmień rozmiar UICollectionViewCell na różnych orientacjach urządzeń


100

Używam UICollectionViewz UICollectionViewFlowLayout.

Rozmiar każdej komórki ustawiam za pomocą

collectionView:layout:sizeForItemAtIndexPath:

Podczas przełączania się z pionowej na poziomą chciałbym dostosować rozmiar każdej komórki, aby całkowicie dopasować rozmiar CollectionView, bez pozostawiania odstępu między komórkami.

Pytania:

1) Jak zmienić rozmiar komórki po zdarzeniu rotacji?

2) A jeszcze lepiej, jak zrobić układ, w którym komórki zawsze mieszczą się na całym ekranie?


Odpowiedź followbena jest obecnie akceptowana, ale ma pewne problemy: wyrzuci błąd konsoli, a animacja do nowego rozmiaru nie będzie działać poprawnie. Zobacz moją odpowiedź dotyczącą prawidłowej implementacji.
memmons

@ MichaelG.Emmons: Twoja odpowiedź będzie działać lepiej, gdy widok kolekcji wyświetla w danym momencie tylko jedną pełnowymiarową komórkę. W takim przypadku ma sens UIKit narzeka na rotację, ponieważ sizeForItemAtIndexPathprzed wywołaniem będzie wysyłał zapytania didRotateFromInterfaceOrientation. Jednak w przypadku widoku kolekcji z wieloma komórkami na ekranie unieważnienie układu przed obróceniem powoduje nieprzyjemny, widoczny skok rozmiaru przed obróceniem widoku. W tym przypadku uważam, że moja odpowiedź jest nadal najbardziej poprawną implementacją.
followben

@followben Zgadzam się, że każdy ma swoje problemy w innych przypadkach użycia. W tym przypadku jednak PO wskazuje, I would like to adjust the size of each cell to **completely fit the size of the CollectionView**które dokładnie rozwiązanie zaproponowałem w mojej odpowiedzi. Gdybyś mógł zawrzeć moje rozwiązanie w swojej odpowiedzi i zanotować, które podejście działa najlepiej w jakich warunkach byłoby to dla mnie wystarczająco dobre.
memmons

Odpowiedzi:


200

1) Możesz obsługiwać i zamieniać wiele obiektów układu, ale jest prostszy sposób. Po prostu dodaj następujące elementy do swojej UICollectionViewControllerpodklasy i dostosuj rozmiary zgodnie z wymaganiami:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{    
    // Adjust cell size for orientation
    if (UIDeviceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
        return CGSizeMake(170.f, 170.f);
    }
    return CGSizeMake(192.f, 192.f);
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.collectionView performBatchUpdates:nil completion:nil];
}

Powołanie -performBatchUpdates:completion: unieważni układ i zmieni rozmiar komórek za pomocą animacji (możesz po prostu przekazać zero do obu parametrów bloku, jeśli nie masz żadnych dodatkowych zmian do wykonania).

2) Zamiast zakodować na stałe rozmiary elementów w -collectionView:layout:sizeForItemAtIndexPath, po prostu podziel wysokość lub szerokość granic collectionView przez liczbę komórek, które chcesz zmieścić na ekranie. Użyj wysokości, jeśli collectionView przewija się w poziomie lub szerokości, jeśli przewija się w pionie.


3
dlaczego po prostu nie zadzwonić [self.collectionView.collectionViewLayout invalidateLayout];?
user102008

7
@ user102008 Wywołanie invalidateLayoutspowoduje ponowne narysowanie komórek o prawidłowym rozmiarze, ale -performBatchUpdates:completion:ożywi zmiany, co wygląda o wiele ładniej IMHO.
followben

4
W moim przypadku kod nie zmienia rozmiaru komórki widoku kolekcji, dopóki nie przewinie się ekranu.
newguy

9
didRotateFromInterfaceOrientationprzestarzałe w iOS 8
János

8
Na systemów iOS 8, dzwonię coordinator.animateAlongsideTransitionw viewWillTransitionToSize, i powołanie performBatchUpdatesw jej animationbloku. Daje to ładny efekt animacji.
Mike Taverne

33

Jeśli nie chcesz, aby dodatkowe animacje zostały wprowadzone performBatchUpdates:completion:, po prostu użyj, invalidateLayoutaby wymusić ponowne załadowanie collectionViewLayout.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

13

Rozwiązałem za pomocą dwóch UICollectionViewFlowLayout. Jeden do portretu i jeden do krajobrazu. Przypisuję je do mojej kolekcji dynamicznie w -viewDidLoad

self.portraitLayout = [[UICollectionViewFlowLayout alloc] init];
self.landscapeLayout = [[UICollectionViewFlowLayout alloc] init];

UIInterfaceOrientation orientationOnLunch = [[UIApplication sharedApplication] statusBarOrientation];

if (UIInterfaceOrientationIsPortrait(orientationOnLunch)) {
    [self.menuCollectionView setCollectionViewLayout:self.portraitLayout];
} else {
    [self.menuCollectionView setCollectionViewLayout:self.landscapeLayout];
}

Następnie po prostu zmodyfikowałem moje metody collectionViewFlowLayoutDelgate w ten sposób

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    CGSize returnSize = CGSizeZero;

    if (collectionViewLayout == self.portraitLayout) {
        returnSize = CGSizeMake(230.0, 120.0);
    } else {
        returnSize = CGSizeMake(315.0, 120.0);
    }

    return returnSize;
}

Ostatnio zmieniam układ na inny podczas obracania

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
    if (UIInterfaceOrientationIsPortrait(fromInterfaceOrientation)) {
        [self.menuCollectionView setCollectionViewLayout:self.landscapeLayout animated:YES];
    } else {
        [self.menuCollectionView setCollectionViewLayout:self.portraitLayout animated:YES];
    }
}

8

Istnieje prosty sposób ustawienia rozmiaru komórki widoku kolekcji:

UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout;
layout.itemSize = CGSizeMake(cellSize, cellSize);

Nie jestem pewien, czy można go animować.


Musiałem użyć tego wewnątrz widokuWillLayoutSubviews z odpowiedzią Hai Feng Kao: stackoverflow.com/a/14776915/2298002
szklarnia

7

Swift: Collection View Cell, czyli n% wysokości ekranu

Potrzebna mi była komórka, która miała określony procent wysokości wyświetleń i będzie zmieniać rozmiar wraz z obrotem urządzenia.

Z pomocą z góry oto rozwiązanie

override func viewDidLoad() {
    super.viewDidLoad()
    automaticallyAdjustsScrollViewInsets = false
    // if inside nav controller set to true
}

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    collectionViewLayout.invalidateLayout()
}

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return CGSize(width: 100, height: collectionView.frame.height * 0.9)
}

2

Jeśli używasz autolayout. Wystarczy, że użyjesz invalidate layoutkontrolera widoku podczas obracania i dostosowywaniaoffset w podklasie układu przepływu.

Więc Twoim zdaniem kontroler -

override func willRotateToInterfaceOrientation(toInterfaceOrientation:     UIInterfaceOrientation, duration: NSTimeInterval) {
    self.galleryCollectionView.collectionViewLayout.invalidateLayout()
}

I w twoim FlowLayout Subclass

override func prepareLayout() {
    if self.collectionView!.indexPathsForVisibleItems().count > 0{
    var currentIndex : CGFloat!
    currentIndex = CGFloat((self.collectionView!.indexPathsForVisibleItems()[0] as! NSIndexPath).row)
    if let currentVal = currentIndex{
        self.collectionView!.contentOffset = CGPointMake(currentIndex * self.collectionView!.frame.width, self.collectionView!.contentOffset.y);
    }   
    }     
}

1
To działa świetnie! willRotateToInterfaceOrientationjest przestarzały w iOS 8, ale viewWillTransitionToSizedziała równie dobrze w tym przypadku.
keegan3d
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.