UICollectionViewFlowLayout не аннулирует право на изменение ориентации

у меня есть UICollectionView с UICollectionViewFlowLayout. Я также реализую протокол UICollectionViewDelegateFlowLayout.

В моем источнике данных у меня есть куча UIViewController, которые реагируют на пользовательский протокол, поэтому я могу запросить их размер и некоторые другие вещи.

В делегате FlowLayout, когда он запрашивает sizeForItemAtIndexPath:, я возвращаю размер элемента, который я получаю из своего протокола. ViewControllers, которые реализуют мой протокол, возвращают разный размер элемента в зависимости от ориентации.

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

    the item width must be less that the width of the UICollectionView minus the section insets left and right values.
    Please check the values return by the delegate.

Он все еще работает, но мне не нравится получать предупреждения, поэтому, возможно, вы можете сказать мне, что я делаю неправильно. Также есть еще одна проблема. Если я не скажу, что мой collectionViews collectionViewLayout аннулируется в willAnimateRotationToInterfaceOrientation: sizeForItemAtIndexPath: никогда не вызывается.

Надеюсь, вы понимаете, что я имею в виду. Если вам нужна дополнительная информация, дайте мне знать :)


person xxtesaxx    schedule 02.05.2013    source источник
comment
У меня такая же проблема, ты разобрался в чем дело?   -  person kevboh    schedule 15.05.2013
comment
еще нет, но я все еще работаю над решением. Если найду, дам знать   -  person xxtesaxx    schedule 17.05.2013


Ответы (7)


Одним из решений является использование KVO и прослушивание изменений в кадре суперпредставления для представления коллекции. Вызов -reloadData сработает и избавит от предупреждений. Здесь есть проблема со временем...

// -loadView
[self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

// -dealloc
[self.view removeObserver:self forKeyPath:@"frame"];

// KVO Method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [collectionView_ reloadData];
}
person kwigbo    schedule 31.05.2013
comment
это может сработать, но кадры UIView не совместимы с KVO согласно документации, ответ Боба Уэйкфилда не зависит от каких-либо взломов. - person myell0w; 23.03.2014

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

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

Я создал подкласс UICollectionViewFlowLayout.

Я переопределяю prepareLayout для пересчета вставок, а затем вызываю [super prepareLayout].

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

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

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // does the superclass do anything at this point?
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    // do whatever else you need before rotating toInterfaceOrientation

    // tell the layout to recalculate
    [self.collectionViewLayout invalidateLayout];
}

UICollectionViewFlowLayout поддерживает положение прокрутки между ориентациями. Та же ячейка будет центрирована только, если она квадратная.

person Bob Wakefield    schedule 18.11.2013
comment
У @cynistersix есть более современный ответ. Интерфейс вращения устарел. Я действительно думаю, что лучше аннулировать макет, чем перезагружать данные. - person Bob Wakefield; 26.04.2015

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

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

    [self.collectionView.collectionViewLayout invalidateLayout];
}
person cynistersix    schedule 11.12.2014
comment
Я предлагаю вам аннулировать макет вместо перезагрузки данных. - person Bob Wakefield; 21.06.2015
comment
У вас должны быть сверхспособности @BobWakefield ... Я уже внес это изменение :) Обновленный ответ - person cynistersix; 23.06.2015

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

Я открываю UIViews программно в зависимости от некоторых событий. Поэтому я не могу использовать - (void) willRotateToInterfaceOrientation: и invalidateLayout или reloadData, потому что:
1. invalidateLayout "происходит во время следующего цикла обновления макета представления".
2. reloadData "Для повышения эффективности представление коллекции отображает только эти ячейки и дополнительные представления. которые видны."

Мне помогло использование ширины родителя collectionView (UIView) в:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
int currentWidth = self.frame.size.width;

UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout sectionInset];
int fixedWidth = currentWidth - (sectionInset.left + sectionInset.right);


По какой-то причине рамка collectionView имела ширину портрета во время вызова этой функции... используя int currentWidth = self.frame.width, помогите мне это исправить.

person MiguelHincapieC    schedule 07.08.2014

Вариант ответа @meelawsh, но в Swift и без свойства self.sizeForCollectionView:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    guard let collectionView = self.collectionView else {
        return
    }

    if size.width > collectionView.frame.width {
        // bigger

        let animation: (UIViewControllerTransitionCoordinatorContext)->(Void) = {
            context in
            collectionView.collectionViewLayout.invalidateLayout()
        }

        coordinator.animateAlongsideTransition(animation, completion: nil)
    } else {
        // smaller
        collectionView.collectionViewLayout.invalidateLayout()
    }
}
person Kenny Winker    schedule 19.11.2015
comment
На самом деле я не чувствую разницы в выполнении coordinator.animateAlongsideTransition(animation, completion: nil). Поэтому я думаю, что только collectionView.collectionViewLayout.invalidateLayout() должно быть достаточно. - person Hlung; 18.03.2016

iOS8: единственный способ отключить предупреждение — вызвать недействительность во время перехода при повороте на больший размер и перед переходом при повороте на меньший размер.

    self.sizeForCollectionView = size; //used to determine collectionview size in sizeForItem
    if (size.width <= size.height) {
        [self.collectionView.collectionViewLayout invalidateLayout];
    }

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        if (size.width > size.height) {
            [self.collectionView.collectionViewLayout invalidateLayout];
        }
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    }];
person meelawsh    schedule 26.05.2015
comment
Не позволяя координатору анимировать переход, простой вызов invalidateLayout у меня не работал. Кажется, это все - person Craig; 10.02.2017

Ответ @ 0mahc0 сработал для меня, но только частично.

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

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

Решение @0mahc0 работает, но я хочу растянуть ячейку до максимальной ширины, поэтому я удалил вставки раздела, иначе у меня будет «маржа» слева и справа.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsZero;
}

Вместо реализации метода делегата вы можете перейти к построителю интерфейса и изменить значение на ноль правой и левой вставок.

person Pedro Romão    schedule 02.04.2015
comment
В построителе интерфейса значения вставки равны нулю, но я получаю эту ошибку. - person Mansuu....; 13.09.2017