Что вызвало эту ошибку iOS? UICollectionView получил атрибуты макета для ячейки с указательным путем, который не существует

Я работаю над приложением, которое имеет UICollectionViewController, который разбивается в определенных таинственных ситуациях, которые трудно воспроизвести. Журнал сбоя выглядит следующим образом:

*** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /SourceCache/UIKit_Sim/UIKit-3318.16.14/UICollectionViewData.m:417
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xc000000000008016> {length = 2, path = 0 - 1}'

Такие сбои, похоже, только что начались в нашем коде после того, как мы переключились на iOS 8 SDK.

Почему это происходит?

Примечание. Я уже знаю, что ответ на вопрос, но я нашел очень мало информации об этом сбое в Qaru и остальной части Интернета. Я отправлю ответ ниже. Эта ошибка заставила моего коллегу и меня три дня выследить, поэтому, надеюсь, этот пост спасет кого-то еще много времени и разочарования. Я зарегистрировал эту ошибку с Apple.

Ответ 1

Авария происходила в следующей ситуации:

У нас был контроллер представления коллекции, который представлял на нем еще один контроллер представления.

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

  • [UICollectionView insertItemsAtIndexPaths:] был вызван с 50 элементы в виде коллекции скрытых UICollectionViewController.
  • [UICollectionView reloadData] был вызван в скрытом виде коллекции.
  • Произошла короткая задержка.
  • Количество элементов в скрытом представлении коллекции было установлено на небольшое число.
  • [UICollectionView reloadData] был вызван снова.
  • Контроллер представления был уволен, показывая скрытый контроллер представления коллекции.

Ошибка подтверждения во внутреннем классе UIKit UICollectionViewData произойдет на шаге 6.

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

Наш метод решения этой проблемы заключался в вызове [UICollectionView reloadSections:] вместо [UICollectionView reloadData] в ключевых точках.

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

Мы думаем, что мы не видели этого поведения, пока не начали создавать наше приложение для iOS 8.

Хорошо спать, друзья!

Ответ 2

Для меня это был массив UICollectionViewLayoutAttribute. Я использую его в UICollectionViewLayout для хранения атрибута элемента.

Я забыл очистить его в методе prepareLayout.

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

Только с removeAll в массиве в начале prepareLayout он работает.

Ответ 3

Я уже некоторое время работаю над этой же ошибкой и думаю, что нашел другой источник ошибок на iOS 7, но работает нормально на iOS 8, что вызывает такую ​​же точную ошибку: Auto-Layout!

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

Содержимое моей коллекции "статично", загружается при загрузке и не дает возможности для незащищенных изменений. Снова это отлично работает с iOS 8.

Итак, я отключил функцию автоматического макета только для этого вида и BINGO! Крушение исчезло. Авто-макет игрался с внутренним UICollectionSize, таким образом, метод (NSArray *) layoutAttributesForElementsInRect: (CGRect) возвращал результаты смешивания.

Я попробовал reloadData, reloadSections, ничего не делает, но это работает!

Надеюсь, что это поможет кому-то другому бороться с исключением UIKit с этим контролем.

Ответ 4

collectionViewLayout кэширует атрибуты. В viewwillappear - создайте новый экземпляр коллекцииViewLayout и назначьте его в collectionview.collectionViewLayout Таким образом, все кэшированные атрибуты будут очищаться до перезагрузки Ваша проблема может быть решена. Работала для меня, особенно когда вы используете другие библиотеки collectionViewLayout.

Ответ 5

Xcode 8 - Swift 3

В моем случае эта ошибка была вызвана Autolayout; У меня есть коллекцияView, встроенная в UIView, которую я скрываю, установив высоту в 0, когда коллекцияView пуст и вернется к 150, когда мне нужно снова показать CollectionView.

Мне удалось удалить ошибку, позвонив

collectionView.collectionViewLayout.invalitdateLayout()

прежде чем я запустил код, который анимирует вызов layoutIfNeeded() в superView. Теперь это довольно гладко.

Надеюсь, это может помочь кому-то в будущем.

var sponsoredPlaceSummaries: [PlaceSummary] = [] {

        didSet {

            if sponsoredPlaceSummaries.isEmpty {

                self.sponsoredPlacesViewHeight.constant = 0
                self.collectionView.collectionViewLayout.invalidateLayout()

                UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseInOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: nil)

            } else if self.sponsoredPlacesViewHeight.constant != 150 {

                self.sponsoredPlacesViewHeight.constant = 150

                UIView.animate(withDuration: 1.0, delay: 0, options: .curveEaseInOut, animations: {
                    self.view.layoutIfNeeded()

                }, completion: nil)
            }
        } 

Ответ 6

это помогло мне:

cell.collectionView.collectionViewLayout.invalidateLayout()
cell.collectionView.reloadData()
cell.collectionView.layoutSubviews()

Ответ 7

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

Итак, я повторно использую тот же UICollectionViewLayout, что и начальный параметр < - Это проблема.

2 UICollectionView должен использовать другой параметр UICollectionViewLayout. Итак, просто новый UICollectionViewLayout для второго UICollectionView.

Ответ 8

Для меня это было связано с ответом @Drew (мой просмотр коллекции был выключен). Я манипулировал ограничением высоты коллекцииView, чтобы свернуть его, когда нет данных. Это вызвало этот крах (иногда!). Я поместил collectionView в другое представление и повторно назначил @IBOutlet этому новому ограничению высоты представления. И сделал высоту коллекцииView постоянной. Crash ушел навсегда!

Ответ 9

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

Ответ 10

Я получал ту же ошибку, но это происходило довольно случайным образом, так как я не мог воссоздать ошибку последовательно. Я использовал customLayout, где я устанавливал атрибуты для элементов вручную. Я бы сделал одну вещь на моем реальном iPhone, который бы разбил ее, но на симуляторе xCode это сработает. То, что в конечном итоге исправляло мою проблему, было использование collectionView.reloadData() вместо collectionView.reloadSections([mySectionNum]). Я понятия не имею, почему он работал иногда, а не другие. Кажется, он должен был рухнуть все время, но это не так. Я также не знаю, почему это исправление работает.