Изменение размера UICollectionViewCell для разных ориентаций устройства


100

Я использую UICollectionViewс UICollectionViewFlowLayout.

Я устанавливаю размер каждой ячейки через

collectionView:layout:sizeForItemAtIndexPath:

При переключении с портретной на альбомную ориентацию я хотел бы настроить размер каждой ячейки, чтобы полностью соответствовать размеру CollectionView, не оставляя отступов между ячейками.

Вопросы:

1) Как изменить размер ячейки после события вращения?

2) А еще лучше, как сделать макет, при котором ячейки всегда умещались на весь размер экрана?


Ответ followben в настоящее время принят, но у него есть некоторые проблемы: он выдаст консольную ошибку, и анимация до нового размера не будет выполняться правильно. См. Мой ответ для правильной реализации.
memmons

@ MichaelG.Emmons: ваш ответ будет работать лучше, если представление коллекции отображает только одну полноразмерную ячейку за раз. В этом случае имеет смысл, что UIKit жалуется на ротацию, поскольку он будет запрашивать sizeForItemAtIndexPathперед вызовом didRotateFromInterfaceOrientation. Однако для представления коллекции с несколькими ячейками на экране недействительность макета перед поворотом вызывает неприятный, видимый скачок в размере перед поворотом представления. В этом случае я считаю, что мой ответ по-прежнему является наиболее правильной реализацией.
followben

@followben Согласитесь, что у каждого есть свои проблемы в разных сценариях использования. Однако в этом случае OP указывает, I would like to adjust the size of each cell to **completely fit the size of the CollectionView**какое именно решение было предложено в моем ответе. Если бы вы могли включить мое решение в свой ответ и отметить, какой подход работает лучше всего, в каких условиях этого было бы достаточно для меня.
memmons

Ответы:


200

1) Вы можете поддерживать и менять несколько объектов макета, но есть более простой способ. Просто добавьте следующее в свой UICollectionViewControllerподкласс и отрегулируйте размеры по мере необходимости:

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

Вызов -performBatchUpdates:completion:аннулирует макет и изменяет размер ячеек с помощью анимации (вы можете просто передать nil обоим параметрам блока, если у вас нет дополнительных настроек для выполнения).

2) Вместо жесткого кодирования размеров элементов в -collectionView:layout:sizeForItemAtIndexPath , просто разделите высоту или ширину границ collectionView на количество ячеек, которые вы хотите разместить на экране. Используйте высоту, если ваш collectionView прокручивается по горизонтали, или ширину, если он прокручивается по вертикали.


3
почему бы просто не позвонить [self.collectionView.collectionViewLayout invalidateLayout];?
user102008

7
@ user102008 Вызов invalidateLayoutбудет перерисовывать ячейки с правильным размером, но -performBatchUpdates:completion:анимирует изменения, что выглядит намного лучше IMHO.
followben

4
В моем случае код не изменяет размер collectionviewcell, пока я не прокручу экран.
newguy

9
didRotateFromInterfaceOrientationустарело в iOS 8
János

8
На iOS8, я звоню coordinator.animateAlongsideTransitionв viewWillTransitionToSizeи вызывая performBatchUpdatesв своем animationблоке. Это дает хороший анимационный эффект.
Майк Таверн,

33

Если вам не нужна дополнительная анимация performBatchUpdates:completion:, просто используйте invalidateLayoutдля принудительной перезагрузки collectionViewLayout.

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

13

Я решил использовать два UICollectionViewFlowLayout. Один для портрета и один для пейзажа. Я назначаю их своей коллекцииView динамически в -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];
}

Затем я просто изменил свои методы collectionViewFlowLayoutDelgate следующим образом

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

Последний раз я переключаюсь с одного макета на другой при ротации

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

8

Есть простой способ установить размер ячейки представления коллекции:

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

Не уверен, что это можно анимировать.


мне пришлось использовать это внутри viewWillLayoutSubviews с ответом Хай Фенга Као: stackoverflow.com/a/14776915/2298002
теплица

7

Swift: ячейка представления коллекции, которая составляет 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)
}

2

Если вы используете. autolayoutВам просто нужно invalidate layoutв контроллере представления при повороте и настройкеoffset в подклассе макета потока.

Итак, в вашем контроллере представления -

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

И в твоем 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
Это прекрасно работает! willRotateToInterfaceOrientationустарела в iOS 8, но viewWillTransitionToSizeработает и для этого.
keegan3d
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.