Modifier la taille de UICollectionViewCell sur différentes orientations de périphérique

100

J'utilise un UICollectionViewavec UICollectionViewFlowLayout.

Je règle la taille de chaque cellule via le

collectionView:layout:sizeForItemAtIndexPath:

Lors du passage du portrait au paysage, je voudrais ajuster la taille de chaque cellule pour qu'elle s'adapte complètement à la taille de CollectionView, sans laisser d'espace de remplissage entre les cellules.

Des questions:

1) Comment changer la taille d'une cellule après un événement de rotation?

2) Et, encore mieux, comment faire la mise en page où les cellules s'adaptent toujours à la taille entière de l'écran?

Fmessina
la source
La réponse de followben est actuellement acceptée, mais elle a quelques problèmes: elle lancera une erreur de console et l'animation à la nouvelle taille ne se déroulera pas correctement. Voir ma réponse pour la mise en œuvre correcte.
memmons
@ MichaelG.Emmons: votre réponse fonctionnera mieux là où une vue de collection n'affiche qu'une seule cellule pleine taille à la fois. Dans ce cas, il est logique que UIKit se plaint de la rotation car il effectuera une requête sizeForItemAtIndexPathavant d'appeler didRotateFromInterfaceOrientation. Cependant, pour une vue de collection avec plusieurs cellules à l'écran, l'invalidation de la mise en page avant la rotation provoque un saut de taille méchant et visible avant la rotation de la vue. Dans ce cas, je pense que ma réponse est toujours la mise en œuvre la plus correcte.
suivez
@followben Convenez que chacun a ses problèmes dans différents cas d'utilisation. Dans ce cas cependant, l'OP indique I would like to adjust the size of each cell to **completely fit the size of the CollectionView**quelle est exactement la solution proposée dans ma réponse. Si vous pouviez inclure ma solution dans votre réponse et noter quelle approche fonctionne le mieux dans quelles conditions cela me suffirait.
memmons

Réponses:

200

1) Vous pouvez conserver et échanger plusieurs objets de mise en page, mais il existe un moyen plus simple. Ajoutez simplement ce qui suit à votre UICollectionViewControllersous-classe et ajustez les tailles selon vos besoins:

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

L'appel -performBatchUpdates:completion:invalidera la mise en page et redimensionnera les cellules avec l'animation (vous pouvez simplement passer nil aux deux paramètres de bloc si vous n'avez aucun ajustement supplémentaire à effectuer).

2) Au lieu de coder en dur les tailles des éléments -collectionView:layout:sizeForItemAtIndexPath, divisez simplement la hauteur ou la largeur des limites de la collectionView par le nombre de cellules que vous souhaitez insérer à l'écran. Utilisez la hauteur si votre collectionView défile horizontalement ou la largeur si elle défile verticalement.

Followben
la source
3
pourquoi ne pas simplement appeler [self.collectionView.collectionViewLayout invalidateLayout];?
user102008
7
@ user102008 Calling invalidateLayoutredessine les cellules avec la bonne taille, mais -performBatchUpdates:completion:animera les changements, ce qui semble beaucoup plus agréable à mon humble avis.
suivez
4
Dans mon cas, le code ne redimensionne pas la collectionviewcell jusqu'à ce que je fasse défiler l'écran.
newguy
9
didRotateFromInterfaceOrientationobsolète dans iOS 8
János
8
Sur iOS8, je vous appelle coordinator.animateAlongsideTransitionà viewWillTransitionToSizeet appeler performBatchUpdatesdans son animationbloc. Cela donne un bel effet d'animation.
Mike Taverne
33

Si vous ne voulez pas d'animations supplémentaires performBatchUpdates:completion:, utilisez simplement invalidateLayoutpour forcer la collectionViewLayout à se recharger.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}
Hai Feng Kao
la source
13

J'ai résolu en utilisant deux UICollectionViewFlowLayout. Un pour le portrait et un pour le paysage. Je les assigne à ma collectionView dinamically dans -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];
}

Ensuite, j'ai simplement modifié ma collectionViewFlowLayoutDelgate Méthodes comme celle-ci

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

Enfin, je passe d'une mise en page à une autre lors de la rotation

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

Il existe un moyen simple de définir la taille de cellule de la vue de collection:

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

Je ne sais pas si c'est animable.

Dannie P
la source
J'ai dû utiliser ceci à l'intérieur de viewWillLayoutSubviews avec la réponse de Hai Feng Kao: stackoverflow.com/a/14776915/2298002
serre
7

Swift: cellule de vue de collection qui représente n% de la hauteur de l'écran

J'avais besoin d'une cellule qui représentait un certain pourcentage de la hauteur des vues et qui serait redimensionnée avec la rotation de l'appareil.

Avec l'aide d'en haut, voici la solution

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)
}
ChienCafé
la source
2

Si vous utilisez autolayout.Vous avez juste besoin de invalidate layoutdans votre contrôleur de vue lors de la rotation et du réglageoffset dans votre sous-classe de disposition de flux.

Donc, dans votre contrôleur de vue -

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

Et dans votre 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);
    }   
    }     
}
toofani
la source
1
Cela fonctionne très bien! willRotateToInterfaceOrientationest obsolète dans iOS 8 mais viewWillTransitionToSizefonctionne aussi bien pour cela.
keegan3d