Предупреждение о размере 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
.
Мы также проверили очевидное; если мы установим высоту ячейки как фиксированный размер, меньший высоты ландшафта, жалоба не возникает. Кроме того, жалоба не возникает при повороте от пейзажа к портрету.
Все работает нормально и корректно отображается. Однако эта жалоба беспокоит нас. У кого-нибудь есть идеи или решения?
Ответы
Ответ 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;
}