SetCollectionViewLayout: анимированная причина ошибки отладки: моментальный снимок, который не был отображен, приводит к пустым моментальным снимкам

Я занят UICollectionView в iOS7.

Я меняю свой макет коллекции между двумя разными макетами. Какие подклассы UICollectionViewFlowLayout.

Вот как я изменяю представления при нажатии кнопки:

-(void)changeViewLayoutButtonPressed
{
    self.changeLayout = !self.changeLayout;

    if (self.changeLayout){
        [self.tableViewLayout invalidateLayout];
        [self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];

    }

    else {

        [self.grideLayout invalidateLayout];
        [self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];

    }
}

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

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

Любые идеи относительно того, что вызывает это?

Ответ 1

Просто добавьте комментарии выше...

Странная вещь о предупреждении, которое я получала, заключалась в том, что у моего CV было три возможных источника данных. Все они были в основном такими же данными, как: сборник 1, сборник 2, сборник 1 и 2.

Когда я просто просматривал коллекцию 1 или коллекцию 2, я не получил никаких предупреждений. Только когда я просматривал двоих, я получил предупреждения. Странная вещь в этом заключается в том, что происходит меньше, чтобы показать все. Коллекция 1 и Коллекция 2 извлекаются из "Целых", поэтому один дополнительный шаг. Не уверен, что это значит.

В любом случае я больше не получаю сообщения.

Вот что я изначально:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self arrangeCollectionView];
}

- (void)arrangeCollectionView
{
    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.cv.collectionViewLayout;
    if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
    {
        flowLayout.sectionInset =  UIEdgeInsetsMake(16, 16, 16, 16);
        flowLayout.minimumInteritemSpacing = 16;
        flowLayout.minimumLineSpacing = 16;
    }
    else
    {
        flowLayout.sectionInset =  UIEdgeInsetsMake(26, 26, 26, 26);
        flowLayout.minimumInteritemSpacing = 26;
        flowLayout.minimumLineSpacing = 26;
    }
    self.cv.collectionViewLayout = flowLayout;    
}

И это давало мне сообщение о повороте в Пейзаж, но не при повороте в портрет.

В обоих этих изменениях больше не было сообщений:

1. Используя

didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

для вызова arrangeCollectionView вместо

willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

Решил проблему предупреждения, но сделал переход немного более грубым.

2. Избавление от явных изменений в UICollectionViewFlowLayout и использование методов DelegateFlowLayout и вызов reloadData из willAnimateRotation...:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self.cv reloadData];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGSize retval = CGSizeMake(172, 256);
    return retval;
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? UIEdgeInsetsMake(16 + 66, 16, 16, 16) : UIEdgeInsetsMake(26 + 66, 26, 26, 26);
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 16 : 28;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 16 : 28;
}

Это сработало лучше для меня. Больше сообщений и довольно плавный переход от одного макета к другому.

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

Удачи, надеюсь, что это поможет.

Ответ 2

Эта проблема со мной тоже. Я решаю это, вызывая метод UIScreen.mainScreen().snapshotViewAfterScreenUpdates(true) до setCollectionViewLayout. Я также не знаю почему. Хотелось бы, чтобы это помогло.

Ответ 3

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

- (UIView*)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
{
    UIGraphicsBeginImageContext(self.bounds.size);

    [self drawRect:self.bounds];

    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIImageView* aView = [[UIImageView alloc] initWithFrame:self.bounds];

    [aView setImage:viewImage];

    return aView;
}

Мне нужно сделать еще несколько тестов и посмотреть, подходит ли это правильный подход, но пока он работает.

Ответ 4

Когда метод - (UIView *)snapshotViewAfterScreenUpdates: в представлении, который еще не отобразил его, генерирует это предупреждение консоли.

Либо вы вызываете этот метод где-то, либо, скорее всего, setCollectionViewLayout: animated: использует этот метод, и представление коллекции не отображается, когда оно используется.

Моя рекомендация состояла бы в том, чтобы не вызвать - (void)invalidateLayout в представлении коллекции и посмотреть, что произойдет.

Ответ 5

Я столкнулся с той же проблемой. Я использовал UICollectionViewFlowLayout в моем представлении коллекции. Я хотел расположить макет в соответствии с ориентацией интерфейса

Как обычно, я переделал willAnimateRotationToInterfaceOrientation:duration.

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
    layout.itemSize = aNewSize;
    [layout invalidateLayout];
}

В консоли появилось одно и то же сообщение. Возможно, анимация сделала снимки неудачными. Думаю, у суб-просмотров не хватило времени.

Впоследствии я попробовал ваше решение. Я использовал методы делегата вместо прямого назначения размера элемента. Однако симулятор все еще сказал мне, что есть предупреждения. Здесь что-то странное. Во время отладки я обнаружил, что консоль не будет отображать предупреждающие сообщения, если общее количество ячеек - только 4. Когда я изменил число до 10, сообщения снова появились. Не знаю, почему.

Продолжим. Я думал, что проблема может возникнуть в результате создания макета во время анимации. Поэтому я переместил код в willRotateToInterfaceOrientation:duration:.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
    layout.itemSize = aNewSize;
    [self.collectionViewLayout invalidateLayout];
} 

Этот метод был вызван до первого. Что еще, сообщение больше не отображалось.

Кстати, если вы упорядочиваете ячейки динамически, вы должны остерегаться изменений в представлениях. В willRotateToInterfaceOrientation:duration: свойство interfaceOrientation и границы просмотра все еще являются старыми значениями. Но когда дело доходит до willAnimateRotationToInterfaceOrientation:duration:, значения были обновлены.

Ответ 6

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

private var isListLayout = true{
    didSet{
        UIScreen.main.snapshotView(afterScreenUpdates: true)
        collectionView!.collectionViewLayout.invalidateLayout()
        if isListLayout {
            DispatchQueue.main.async {
               self.collectionView!.setCollectionViewLayout(ListLayout(), animated: true)
            }
        }else{
            DispatchQueue.main.async {
                self.collectionView!.setCollectionViewLayout(GridLayout(), animated: true)
            }
        }
    }
}

Если вы игнорируете var, ключевой момент - вызов метода: UIScreen.main.snapshotView(afterScreenUpdates: true) а также обновление нового макета в следующем основном фрейме: DispatchQueue.main.async { self.collectionView!.setCollectionViewLayout(ListLayout(), animated: true) } Это прекрасно работает для меня без проблем отладки и прерывистой анимации с перерывами

Ответ 7

Поместите функцию collectionView layoutIfNeeded() в DispatchQueue.main.async сразу после аннулирования макета.

    let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout

    flowLayout?.invalidateLayout()

    DispatchQueue.main.async { [weak self] in
          self?.collectionView.layoutIfNeeded()
    }

Ответ 8

У меня был вид коллекции с изменяемыми размерами ячеек, которые выдавали это предупреждение всякий раз, когда я поворачивал ориентацию устройства. Если ячейка была видна после поворота, но не перед вращением, устройство все равно попыталось бы сделать снимок версии ячейки перед вращением, которая еще не была визуализирована. Поэтому я закончил проверку моего контроллера представления коллекции, чтобы увидеть, какие ячейки видны до вращения. Это для вертикально прокручиваемых видов коллекций, но вы можете просто поменять местами ссылки на x, y, minX, minY и т.д., Чтобы они работали для горизонтальных видов коллекций.

override func viewWillTransition(
    to size: CGSize,
    with coordinator: UIViewControllerTransitionCoordinator)
{
    super.viewWillTransition(to: size, with: coordinator)
    let contentOffset = collectionView.contentOffset

    let visibleFrame = view.safeAreaLayoutGuide.layoutFrame

    collectionView.visibleCells.forEach { cell in

        guard let customCell = cell as? CustomCell else {
            return
        }

        let minY = customCell.frame.minY - contentOffset.y
        let maxY = customCell.frame.maxY - contentOffset.y

        if
            visibleFrame.contains(CGPoint(x: visibleFrame.minX, y: minY)) ||
            visibleFrame.contains(CGPoint(x: visibleFrame.minX, y: maxY))
        {
            customCell.shouldSnapshot = true
        }
    }

}

где пользовательская ячейка была подклассом UICollectionViewCell со следующим:

class CustomCell: UICollectionViewCell {

    var shouldSnapshot = false

    override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
        if shouldSnapshot {
            shouldSnapshot = false
            return super.snapshotView(afterScreenUpdates: afterUpdates)
        } else {
            return nil
        }
    }
}

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