Modificare le dimensioni di UICollectionViewCell su diversi orientamenti del dispositivo


100

Sto usando un UICollectionViewcon UICollectionViewFlowLayout.

Ho impostato la dimensione di ogni cella tramite

collectionView:layout:sizeForItemAtIndexPath:

Quando si passa da verticale a orizzontale, vorrei regolare la dimensione di ogni cella per adattarla completamente alle dimensioni di CollectionView, senza lasciare spazio di riempimento tra le celle.

Domande:

1) Come modificare la dimensione di una cella dopo un evento di rotazione?

2) E, ancora meglio, come realizzare il layout in cui le celle si adattano sempre all'intera dimensione dello schermo?


La risposta di followben è attualmente accettata, ma presenta alcuni problemi: genererà un errore della console e l'animazione alla nuova dimensione non avverrà correttamente. Vedi la mia risposta per la corretta implementazione.
memmons

@ MichaelG.Emmons: la tua risposta funzionerà meglio quando una visualizzazione raccolta mostra solo una singola cella a grandezza naturale alla volta. In tal caso, ha senso che UIKit si lamenta a rotazione poiché eseguirà una query sizeForItemAtIndexPathprima di chiamare didRotateFromInterfaceOrientation. Tuttavia, per una vista della raccolta con più celle sullo schermo, l'invalidazione del layout prima della rotazione provoca un brutto e visibile aumento delle dimensioni prima che la vista ruoti. In quel caso, credo che la mia risposta sia ancora l'implementazione più corretta.
followben

@followben Concordo sul fatto che ognuno ha i propri problemi in diversi casi d'uso. In questo caso, però, il PO indica I would like to adjust the size of each cell to **completely fit the size of the CollectionView**qual è esattamente la soluzione proposta nella mia risposta. Se potessi includere la mia soluzione nella tua risposta e prendere nota di quale approccio funziona meglio e in quali condizioni sarebbe abbastanza buono per me.
memmons

Risposte:


200

1) È possibile mantenere e sostituire più oggetti del layout, ma esiste un modo più semplice. Basta aggiungere quanto segue alla tua UICollectionViewControllersottoclasse e regolare le dimensioni come richiesto:

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

La chiamata -performBatchUpdates:completion:invaliderà il layout e ridimensionerà le celle con l'animazione (puoi semplicemente passare zero a entrambi i parametri del blocco se non hai ulteriori regolazioni da eseguire).

2) Invece di codificare le dimensioni dell'elemento -collectionView:layout:sizeForItemAtIndexPath, dividi semplicemente l'altezza o la larghezza dei limiti della collectionView per il numero di celle che desideri adattare sullo schermo. Usa l'altezza se la tua collectionView scorre orizzontalmente o la larghezza se scorre verticalmente.


3
perché non chiamare [self.collectionView.collectionViewLayout invalidateLayout];?
user102008

7
@ user102008 La chiamata invalidateLayoutridisegnerà le celle con la dimensione corretta, ma -performBatchUpdates:completion:animerà le modifiche, il che sembra molto più bello IMHO.
followben

4
Nel mio caso il codice non ridimensiona la collectionviewcell finché non scorro lo schermo.
newguy

9
didRotateFromInterfaceOrientationdeprecato in iOS 8
János

8
Su iOS 8, chiamo coordinator.animateAlongsideTransitionin viewWillTransitionToSize, e chiamando performBatchUpdatesnel suo animationblocco. Questo dà un bell'effetto di animazione.
Mike Taverne

33

Se non vuoi aggiungere animazioni aggiuntive performBatchUpdates:completion:, usa semplicemente invalidateLayoutper forzare il ricaricamento di collectionViewLayout.

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

13

Ho risolto utilizzando due UICollectionViewFlowLayout. Uno per il ritratto e uno per il paesaggio. Li assegno dinamicamente alla mia collectionView in -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];
}

Quindi ho semplicemente modificato i miei metodi collectionViewFlowLayoutDelgate come questo

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

Per ultimo passo da un layout all'altro a rotazione

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

8

C'è un modo semplice per impostare la dimensione della cella della vista della raccolta:

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

Non sono sicuro che sia animabile però.


Ho dovuto usare questo all'interno di viewWillLayoutSubviews con la risposta di Hai Feng Kao: stackoverflow.com/a/14776915/2298002
serra

7

Swift: Collection View Cell che è n% dell'altezza dello schermo

Avevo bisogno di una cella che fosse una certa percentuale dell'altezza delle visualizzazioni e che si ridimensionasse con la rotazione del dispositivo.

Con l'aiuto dall'alto, ecco la soluzione

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

Se stai usando autolayout. Devi solo farlo invalidate layoutnel controller della vista durante la rotazione e regolare offsetnella sottoclasse del layout di flusso.

Quindi, nel tuo controller di visualizzazione -

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

E nel tuo 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
Funziona benissimo! willRotateToInterfaceOrientationè deprecato in iOS 8 ma viewWillTransitionToSizefunziona altrettanto bene per questo.
keegan3d
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.