Змініть розмір UICollectionViewCell в різних орієнтаціях пристроїв


100

Я використовую UICollectionViewс UICollectionViewFlowLayout.

Я встановлюю розмір кожної комірки через

collectionView:layout:sizeForItemAtIndexPath:

Перемикаючись з портретного на ландшафтний, я хотів би відрегулювати розмір кожної комірки, щоб повністю відповідати розміру CollectionView, не залишаючи пробілу між клітинками.

Запитання:

1) Як змінити розмір комірки після події обертання?

2) І, ще краще, як зробити макет там, де комірки завжди підходять під весь розмір екрана?


Відповідь followben на даний момент прийнята одна, але у неї є деякі проблеми: вона видасть помилку консолі, і анімація до нового розміру не відбудеться правильно. Дивіться мою відповідь щодо правильної реалізації.
пам’ятки

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

@followben Погодьтеся, що кожен має свої проблеми в різних випадках використання. Однак у цьому випадку ОП вказує, I would like to adjust the size of each cell to **completely fit the size of the CollectionView**яке саме рішення було запропоновано у моїй відповіді. Якщо ви зможете включити моє рішення у свою відповідь і зауважити, який підхід найкраще працює, за яких умов для мене це буде досить добре.
пам’ятки

Відповіді:


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:призведе до недійсності макета та зміни розміру комірок з анімацією (ви можете просто передати нуль обом блокам парам, якщо у вас немає додаткових коригувань для виконання).

2) Замість жорсткого кодування розмірів предметів у -collectionView:layout:sizeForItemAtIndexPath, просто розділіть висоту або ширину меж collectionView на кількість комірок, які ви хочете розмістити на екрані. Використовуйте висоту, якщо ваша колекція View прокручує горизонтально, або ширину, якщо вона прокручується вертикально.


3
чому б не просто подзвонити [self.collectionView.collectionViewLayout invalidateLayout];?
користувач102008

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щоб змусити колекціюViewLayout перезавантажити.

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

Тоді я просто змінив свої колекціїViewFlowLayoutDelgate методи таким чином

- (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.