Cambiar el tamaño de UICollectionViewCell en diferentes orientaciones de dispositivo

100

Estoy usando un UICollectionViewwith UICollectionViewFlowLayout.

Establecí el tamaño de cada celda a través del

collectionView:layout:sizeForItemAtIndexPath:

Al cambiar de retrato a paisaje, me gustaría ajustar el tamaño de cada celda para que se ajuste completamente al tamaño de CollectionView, sin dejar espacio de relleno entre las celdas.

Preguntas:

1) ¿Cómo cambiar el tamaño de una celda después de un evento de rotación?

2) Y, aún mejor, ¿cómo hacer el diseño donde las celdas siempre quepan en todo el tamaño de la pantalla?

Fmessina
fuente
La respuesta de followben es actualmente aceptada, pero tiene algunos problemas: arrojará un error de consola y la animación al nuevo tamaño no sucederá correctamente. Vea mi respuesta para la implementación correcta.
memmons
@ MichaelG.Emmons: su respuesta funcionará mejor cuando una vista de colección solo muestre una celda de tamaño completo a la vez. En ese caso, tiene sentido que UIKit se queje de la rotación, ya que consultará sizeForItemAtIndexPathantes de llamar didRotateFromInterfaceOrientation. Sin embargo, para una vista de colección con varias celdas en pantalla, invalidar el diseño antes de la rotación provoca un salto visible y desagradable en el tamaño antes de que la vista gire. En ese caso, creo que mi respuesta sigue siendo la implementación más correcta.
Followben
@followben Estoy de acuerdo en que cada uno tiene sus problemas en diferentes casos de uso. Sin embargo, en este caso, el OP indica I would like to adjust the size of each cell to **completely fit the size of the CollectionView**cuál es exactamente la solución propuesta en mi respuesta. Si pudiera incluir mi solución en su respuesta y tomar nota de qué enfoque funciona mejor en qué condiciones, sería lo suficientemente bueno para mí.
memmons

Respuestas:

200

1) Puede mantener e intercambiar varios objetos de diseño, pero hay una forma más sencilla. Simplemente agregue lo siguiente a su UICollectionViewControllersubclase y ajuste los tamaños según sea necesario:

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

Llamar -performBatchUpdates:completion:invalidará el diseño y cambiará el tamaño de las celdas con animación (puede pasar nulo a ambos parámetros de bloque si no tiene ajustes adicionales que realizar).

2) En lugar de codificar los tamaños de los elementos -collectionView:layout:sizeForItemAtIndexPath, simplemente divida la altura o el ancho de los límites de collectionView por la cantidad de celdas que desea que quepan en la pantalla. Use la altura si su collectionView se desplaza horizontalmente o el ancho si se desplaza verticalmente.

Followben
fuente
3
¿Por qué no llamar [self.collectionView.collectionViewLayout invalidateLayout];?
user102008
7
@ user102008 La llamada invalidateLayoutvolverá a dibujar las celdas con el tamaño correcto, pero -performBatchUpdates:completion:animará los cambios, lo que se ve mucho mejor en mi humilde opinión.
followben
4
En mi caso, el código no cambia el tamaño de la celda de vista de colección hasta que me desplazo por la pantalla.
Newguy
9
didRotateFromInterfaceOrientationobsoleto en iOS 8
János
8
En iOS8, estoy llamando coordinator.animateAlongsideTransitionen viewWillTransitionToSize, y llamando performBatchUpdatesdentro de su animationbloque. Esto le da un bonito efecto de animación.
Mike Taverne
33

Si no desea que le traigan animaciones adicionales performBatchUpdates:completion:, use invalidateLayoutpara forzar la recarga de collectionViewLayout.

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

Resolví usando dos UICollectionViewFlowLayout. Uno para retrato y otro para paisaje. Los asigno a mi collectionView dinámicamente en -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];
}

Luego simplemente modifiqué mis métodos collectionViewFlowLayoutDelgate como este

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

Por último, cambio de un diseño a otro en rotación

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

Hay una forma sencilla de establecer el tamaño de celda de la vista de colección:

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

Sin embargo, no estoy seguro de si es animable.

Dannie P
fuente
Tuve que usar esto dentro de viewWillLayoutSubviews con la respuesta de Hai Feng Kao: stackoverflow.com/a/14776915/2298002
invernadero
7

Swift: celda de vista de colección que es n% de la altura de la pantalla

Necesitaba una celda que tuviera un cierto porcentaje de la altura de las vistas y que cambiara de tamaño con la rotación del dispositivo.

Con la ayuda de arriba, aquí está la solución.

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)
}
PerroCafé
fuente
2

Si está usando autolayout.Solo necesita hacerlo invalidate layouten su controlador de vista al rotar y ajustar offseten su subclase de diseño de flujo.

Entonces, en su controlador de vista -

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

Y en tu 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
fuente
1
¡Esto funciona muy bien! willRotateToInterfaceOrientationestá obsoleto en iOS 8, pero viewWillTransitionToSizefunciona igual de bien para esto.
keegan3d