Alterar o tamanho UICollectionViewCell em diferentes orientações do dispositivo

100

Estou usando um UICollectionViewcom UICollectionViewFlowLayout.

Eu defino o tamanho de cada célula através do

collectionView:layout:sizeForItemAtIndexPath:

Ao mudar de retrato para paisagem, gostaria de ajustar o tamanho de cada célula para caber completamente no tamanho de CollectionView, sem deixar espaço de preenchimento entre as células.

Questões:

1) Como alterar o tamanho de uma célula após um evento de rotação?

2) E, melhor ainda, como fazer o layout onde as células sempre cabem no tamanho total da tela?

Fmessina
fonte
A resposta de followben é atualmente aceita, mas tem alguns problemas: ela vai lançar um erro de console e a animação para o novo tamanho não vai acontecer corretamente. Veja minha resposta para a implementação correta.
memmons de
@ MichaelG.Emmons: sua resposta funcionará melhor onde uma exibição de coleção exibe apenas uma única célula de tamanho completo por vez. Nesse caso, faz sentido o UIKit reclamar na rotação, pois ele fará uma consulta sizeForItemAtIndexPathantes de chamar didRotateFromInterfaceOrientation. No entanto, para uma exibição de coleção com várias células na tela, invalidar o layout antes da rotação causa um salto desagradável e visível no tamanho antes da rotação da exibição. Nesse caso, acredito que minha resposta ainda é a implementação mais correta.
followben
@followben Concorda que cada um tem seus problemas em diferentes casos de uso. Neste caso, porém, o OP indica I would like to adjust the size of each cell to **completely fit the size of the CollectionView**qual é exatamente a solução proposta em minha resposta. Se você pudesse incluir minha solução em sua resposta e anotar qual abordagem funciona melhor sob quais condições, isso seria bom o suficiente para mim.
memmons

Respostas:

200

1) Você pode manter e trocar vários objetos de layout, mas há uma maneira mais simples. Basta adicionar o seguinte à sua UICollectionViewControllersubclasse e ajustar os tamanhos conforme necessário:

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

A chamada -performBatchUpdates:completion:invalidará o layout e redimensionará as células com animação (você pode apenas passar nil para ambos os parâmetros de bloco se não houver ajustes extras para realizar).

2) Em vez de codificar os tamanhos dos itens -collectionView:layout:sizeForItemAtIndexPath, apenas divida a altura ou largura dos limites do collectionView pelo número de células que você deseja ajustar na tela. Use a altura se sua collectionView rolar horizontalmente ou a largura se rolar verticalmente.

seguir
fonte
3
porque não ligar [self.collectionView.collectionViewLayout invalidateLayout];?
user102008
7
@ user102008 A chamada invalidateLayoutirá redesenhar as células com o tamanho correto, mas -performBatchUpdates:completion:irá animar as alterações, o que parece muito mais agradável IMHO.
followben
4
No meu caso, o código não redimensiona a célula de visualização da coleção até que eu role a tela.
newguy
9
didRotateFromInterfaceOrientationobsoleto no iOS 8
János
8
Em iOS8, eu estou chamando coordinator.animateAlongsideTransitionnos viewWillTransitionToSize, e chamando performBatchUpdatesdentro do seu animationbloco. Isso dá um bom efeito de animação.
Mike Taverne
33

Se você não quiser animações adicionais performBatchUpdates:completion:, basta usar invalidateLayoutpara forçar o recarregamento de collectionViewLayout.

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

Resolvi usando dois UICollectionViewFlowLayout. Um para retrato e outro para paisagem. Eu os atribuo à minha coleção, ver dinamicamente em -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];
}

Então, simplesmente modifiquei meus 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;
}

Da última vez, mudo de um layout para outro na rotação

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

Há uma maneira simples de definir o tamanho da célula de visualização da coleção:

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

Não tenho certeza se é animável.

Dannie P
fonte
tive que usar isso dentro do viewWillLayoutSubviews com a resposta de Hai Feng Kao: stackoverflow.com/a/14776915/2298002
estufa
7

Swift: célula de visualização de coleção que tem n% da altura da tela

Eu precisava era de uma célula que tivesse uma certa porcentagem da altura das visualizações e fosse redimensionada com a rotação do dispositivo.

Com a ajuda de cima, aqui está a solução

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)
}
DogCoffee
fonte
2

Se você estiver usando autolayout.Você só precisa invalidate layoutem seu controlador de visualização ao girar e ajustar offsetem sua subclasse de layout de fluxo.

Então, em seu controlador de visão -

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

E no seu 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
fonte
1
Isso funciona muito bem! willRotateToInterfaceOrientationestá obsoleto no iOS 8, mas também viewWillTransitionToSizefunciona bem para isso.
keegan3d