it-roy-ru.com

UICollectionViewFlowLayout Size Предупреждение при повороте устройства в альбомную ориентацию

Мы используем UICollectionView для отображения ячейки, которая покрывает весь экран (за исключением состояния и навигационной панели). Размер ячейки устанавливается из self.collectionView.bounds.size:

- (void)viewWillAppear:(BOOL)animated
{
    //
    // value isn't correct with the top bars until here
    //
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
}

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    return _currentCellSize;
}

Это устанавливает правильный размер для каждого устройства. В каждой ячейке нет вставок, а в макете нет верхнего или нижнего колонтитула. Однако, когда мы поворачиваемся от портрета к ландшафту, мы получаем следующую «жалобу»:

the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.

Теперь я понимаю эту ошибку, однако мы сбрасываем размер ячейки и используем макеты потоков, встроенные в ротационный переход:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    //self.collectionView.bounds are still the last size...not the new size here
}
- (void)didRotateFromInterfaceOrientation: UIInterfaceOrientation)fromInterfaceOrientation
{
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
    [self.collectionView performBatchUpdates:nil completion:nil];//this will force the redraw/size of the cells.
}

Ячейки правильно отображаются в ландшафте.

Кажется, что Flow Layout видит старый размер ячейки (что вызывает жалобу, поскольку он будет слишком высоким), но читает/отображает новый размер ячейки, установленный в didRotateFromInterfaceOrientation.

Есть ли способ избавиться от жалобы?

Мы попытались найти другую ловушку во время перехода устройства с поворотом, который имеет доступ к правильному целевому размеру экрана (против текущего размера экрана), но безуспешно. Результаты отладки показывают, что жалоба возникает после willRotateToInterfaceOrientation, но до didRotateFromInterfaceOrientation.

Мы также проверили очевидное; если мы установим высоту ячейки равной фиксированному размеру меньше высоты экрана, жалоб не будет. Кроме того, жалоба не возникает при повороте из пейзажа обратно в портрет.

Все работает нормально и правильно отображает. Однако эта жалоба нас беспокоит. У кого-нибудь еще есть идеи или решения?

36
Mikel Nelson

Я получил то же самое предупреждение. Неудовлетворенный подходом «reloadData», я обнаружил, что вызов [self.collectionView.collectionViewFlowLayout invalidateLayout] before настройка фрейма представления коллекции замалчивает предупреждение и дает ожидаемые результаты. 

36
Chris Hinkle

Не бросать еще одну креветку на этого загруженного, но неприемлемого, Барби.

- (CGSize)collectionView:(UICollectionView *)collectionView 
                  layout:(UICollectionViewLayout *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    [collectionView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
    return CGSizeMake(100, collectionView.frame.size.height);
}

Установка содержимого вставок непосредственно перед возвратом размера ячейки сделала свое дело для меня.

Замечания:

Я использую представление контейнера в раскадровке, чтобы загрузить представление коллекции в UIViewController. Я попытался установить это для объекта flowLayout в раскадровке. Представление коллекции в раскадровке. И переопределяя один из UICollectionViewDelegateFlowLayout; хотя я не помню какой. Я также не уверен, будет ли это работать для вертикальной компоновки.

9
Josh Bruce

В 

[UIViewController willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration]

Я позвонил, [UICollectionViewLayout invalidateLayout] и, кажется, работает хорошо.

6
somebody

Я решил это.

Вы должны просто позволить высоте flowLayout меньше, чем collcetionView.frame.size.height.

 [flowLayout setItemSize:CGSizeMake(width, height)];


 collectionView.frame = CGRectMake(12, 380, 290, 80);
4
Donny

Многие решения предлагают добавить invalidateLayout к willAnimateRotationToInterfaceOrientation, но это не рекомендуется с iOS 8.

Для iOS 8 и выше используйте:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [self.collectionView.collectionViewLayout invalidateLayout];
}

Благодаря комментарию @ user7097242 вот версия Swift4:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    self.collectionView.collectionViewLayout.invalidateLayout()
}
4
JosephH

Это работает для меня: (и надеюсь, что это также работает для вас!)

- (void)viewWillLayoutSubviews 
{     
     self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height);    
     [self.flowLayout invalidateLayout];
}

- (void)viewDidLayoutSubviews     
{
    self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height); 
}
2
mayqiyue

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

#define NUMBER_OF_CELLS_PER_ROW 1

- (UICollectionViewFlowLayout *)flowLayout {
    return (UICollectionViewFlowLayout *)self.collectionViewLayout;
}

- (CGSize)itemSizeInCurrentOrientation {
    CGFloat windowWidth = self.collectionView.window.bounds.size.width;

    CGFloat width = (windowWidth - (self.flowLayout.minimumInteritemSpacing * (NUMBER_OF_CELLS_PER_ROW - 1)) - self.flowLayout.sectionInset.left - self.flowLayout.sectionInset.right)/NUMBER_OF_CELLS_PER_ROW;

    CGFloat height = 80.0f;

   return CGSizeMake(width, height);
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.flowLayout invalidateLayout];
    self.flowLayout.itemSize = [self itemSizeInCurrentOrientation];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // Now that the rotation is complete, load the cells.
    [self.collectionView reloadData];
}
2
lambmj

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

Я настроил свое представление (которое содержит UICollectionView), чтобы получать уведомление UIDeviceOrientationDidChangeNotification. В методе, который отвечает на это уведомление, после того, как кадр UICollectionView был скорректирован, я сделал следующее:

- (void)orientationChanged:(NSNotification *)notification
{
    CGSize size = (CGSize){self.frame.size.width - 2*kContentMargin, self.frame.size.height - 2*kContentMargin};
    [self.collectionView.collectionViewLayout setItemSize:size];

    [self.collectionView.collectionViewLayout invalidateLayout];
    [self.collectionView reloadData];
}

Обратите внимание, что кадр UICollectionView устанавливается автоматически при вращении, потому что его resizingMask устанавливается на это при инициализации:

self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
2
Alfie Hanssen

Это решило это для меня:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

И я использую этот метод делегата для установки размера:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

И я могу оттуда использовать это с правильным кадром после поворота:

self.collectionView.frame.size
2
ullstrm

Мое исправление было таким же простым, как снятие отметки «Настроить вставки вида прокрутки» для контроллера представления в IB, поскольку мне нужно было, чтобы моя панель навигации была полупрозрачной.

2
Rick van der Linde

Я столкнулся с той же проблемой . Вот как я ее решил. Надеюсь, поможет

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(    NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    [self.collectionView reloadData];
}
1
xun

Попробуй это...

UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout;

layout.itemSize = CGSizeMake(0.1, 0.1);

Это работает для меня.

1
Masataga.Takashi

Попытка найти решение, чтобы заставить замолчать эти предупреждения на iOS 7, оказалась трудной для меня. Я закончил тем, что прибегнул к подклассу своего UICollectionView и добавил следующий код.

- (void)setFrame:(CGRect)frame
{
    if (!iOS8 && (frame.size.width != self.frame.size.width))
    {
        [self.collectionViewLayout invalidateLayout];
    }

    [super setFrame:frame];
}

Некоторые могут захотеть сделать полную проверку размера с помощью CGSizeEqualToSize().

0
Ricky

Я столкнулся с той же проблемой при изменении размера кадра UICollectionView. Если бы я использовал метод делегата для FlowLayout, чтобы вернуть размер ячейки (который будет обновлен на основе размера содержащего UICollectionView), я бы получил сообщение об ошибке, когда я изменил (уменьшил) кадр UICollectionView, так как он Похоже, не спрашивал делегата об обновленной информации о размере, прежде чем жаловаться. В конечном итоге он будет запрашивать у метода делегата информацию о размере при перерисовке, но все равно выдаст предупреждение, когда я назначу новый метод кадра. Чтобы избавиться от предупреждения, я явно установил свойство itemSize объекта UICollectionViewFlowLayout перед я установил для фрейма новое меньшее значение. Моя ситуация достаточно проста, и я думаю, что на этом этапе я могу сойти с вычислением itemSize (поскольку все мои элементы в представлении коллекции имеют одинаковый размер), а не в зависимости от метода делегата. Простая установка свойства itemSize, оставляя реализованный метод делегата, решила не / решила проблему, так как я думаю, что она игнорировала значение itemSize, если обнаружила, что метод делегата был реализован (если он знает, что он есть, почему неужели это так ?!) Надеюсь, это поможет - возможно, вы также можете явно установить itemSize до поворота.

0
Laura

попробуйте в конце didRotateFromInterfaceOrientation:

[self.collectionView setNeedsLayout]

если это не работает, попробуйте переместить все эти вещи из didRotateFromInterfaceOrientation в willRotateToInterfaceOrientation

0
J.Williams

Я использовал переменную UICollectionViewFlowLayoutInvalidationContext, в которой я вычисляю новое смещение так, чтобы оно сохраняло то же смещение содержимого. Моя собственная функция collectionViewSizeForOrientation: возвращает правильный размер. Это не идеально, но, по крайней мере, не схематично:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    CGSize fromCollectionViewSize = [self collectionViewSizeForOrientation:[self interfaceOrientation]];
    CGSize toCollectionViewSize = [self collectionViewSizeForOrientation:toInterfaceOrientation];

    CGFloat currentPage = [_collectionView contentOffset].x / [_collectionView bounds].size.width;
    NSInteger itemCount = [_collectionView numberOfItemsInSection:0];

    UICollectionViewFlowLayoutInvalidationContext *invalidationContext = [[UICollectionViewFlowLayoutInvalidationContext alloc] init];

    [invalidationContext setContentSizeAdjustment:CGSizeMake(toCollectionViewSize.width * itemCount - fromCollectionViewSize.width * itemCount, toCollectionViewSize.height - fromCollectionViewSize.height)];
    [invalidationContext setContentOffsetAdjustment:CGPointMake(currentPage * toCollectionViewSize.width - [_collectionView contentOffset].x, 0)];

    [[_collectionView collectionViewLayout] invalidateLayoutWithContext:invalidationContext];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

collectionViewSizeForOrientation: в моем случае выглядит следующим образом, предполагая, что вставки и расстояние между элементами равны 0:

- (CGSize)collectionViewSizeForOrientation:(UIInterfaceOrientation)orientation
{
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;

    CGFloat width = UIInterfaceOrientationIsLandscape(orientation) ? MAX(screenSize.width, screenSize.height) : MIN(screenSize.width, screenSize.height);
    CGFloat height = UIInterfaceOrientationIsLandscape(orientation) ? MIN(screenSize.width, screenSize.height) : MAX(screenSize.width, screenSize.height);

    return CGSizeMake(width, height);

}

0
MrJre

Вы можете создать подкласс UICollectionView и переопределить setBounds:. Там перед вызовом [super setBounds:] размер элемента можно отрегулировать до новых границ.
Вы должны проверить, изменился ли размер границ, потому что setBounds: также вызывается при прокрутке.

0
Eddie Gasparian

Примечание. В моем случае я обнаружил, что мы задали свойствоpreferredContentSizeрассматриваемой UIViewController. Если вы считаете, что это ваш случай, вам, возможно, придется столкнуться со следующим методом протокола UICollectionViewDelegateFlowLayout:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize size = collectionViewLayout.collectionView.bounds.size;
    ...
    return size;
}
0
Coach Roebuck

Следующий код исправил это для меня:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
0
Obj-Swift

Просто чтобы подавить предупреждение и (возможно, не уверен) улучшить производительность, которую вы могли бы перед возвратом размера в

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

проверьте, находится ли контроллер представления в процессе вращения и возвращает ли он какой-то относительно небольшой размер, например CGSizeMake(0.1, 0.1)

0
Vladimir Shutyuk

Я знаю, что это старый вопрос, но я получил ту же проблему и потратил час на ее решение. Моя проблема заключалась в том, что кажется, что размер кадра UICollectionView всегда неправильный (высота не соответствует контейнеру), в то время как я устанавливаю размер кадра прямо перед вызовом делегата макета потока UICollectionView. Итак, я снова установил размер фрейма UICollectionView для метода:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

И это помогло. Высота теперь отображается правильно, и предупреждение исчезло.

0
tyegah123