Подтвердить что ты не робот

Предупреждение о размере UICollectionViewFlowLayout при повороте устройства в ландшафт

Мы используем 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.

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Ответ 2

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

- (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; хотя я не помню, какой из них. Я также не уверен, что это будет работать для вертикальной компоновки.

Ответ 3

В

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

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

Ответ 4

Я решил это.

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

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


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

Ответ 5

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

Я установил свой просмотр (тот, который содержит 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 устанавливается автоматически после поворота, потому что его изменение размера маскируется при инициализации:

self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

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

Ответ 10

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

Для iOS 8/9 используйте:

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

    [self.collectionView.collectionViewLayout invalidateLayout];
}

Ответ 11

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

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

    [self.collectionView reloadData];
}

Ответ 12

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

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

layout.itemSize = CGSizeMake(0.1, 0.1);

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

Ответ 13

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

Ответ 14

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

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

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

Я использовал 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);

}

Ответ 18

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

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

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

Ответ 19

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

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

    [super setFrame:frame];
}

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

Ответ 20

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

[self.collectionView setNeedsLayout]

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

Ответ 21

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

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