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

Ошибка iOS 10: UICollectionView получил атрибуты макета для ячейки с пути указателя, который не существует

Запуск моего приложения в устройстве с iOS 10 Я получаю эту ошибку:

UICollectionView получает атрибуты макета для ячейки с отсутствием пути указателя

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

->Table view 
    ->Each cell of table is a custom collection view [GitHub Repo][1]
        ->Each item of collection view has another collection view

Я попытался вставить

    [self.collectionView.collectionViewLayout invalidateLayout];

В

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

обоих представлений коллекции.

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

Может ли кто-нибудь дать мне несколько советов?

4b9b3361

Ответ 1

Это случилось со мной, когда число ячеек в collectionView изменилось. Оказывается, мне не хватало invalidateLayout после вызова reloadData. После добавления, я больше не испытывал сбоев. Apple внесла некоторые изменения в collectionViews в iOS10. Я предполагаю, что причина, по которой мы не сталкиваемся с такой же проблемой в более старых версиях.

Здесь мой последний код:

[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];

Ответ 2

Если у вас есть пользовательский макет, не забудьте очистить кэш (UICollectionViewLayoutAttributes) при переопределении функции prepare.

    override func prepare() {
       super.prepare()
       cache.removeAll()
    }

Ответ 3

Я также столкнулся с этой проблемой с 2 ​​коллекциями в одном представлении, потому что я добавил тот же UICollectionViewFlowLayout в обеих коллекциях:

let layout = UICollectionViewFlowLayout()
collectionView1.collectionViewLayout = layout
collectionView2.collectionViewLayout = layout

Конечно, он сбрасывается при перезагрузке данных, если CollectionView1 Data изменяется. Если это может помочь кому-то.

Ответ 4

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

 UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
 layout.estimatedItemSize = CGSizeMake(60, 25);
 layout.itemSize = UICollectionViewFlowLayoutAutomaticSize;             
 self.collectionView.collectionViewLayout = layout;

Я решаю эти проблемы, заменяя

[self.collectionView reloadData];

к

[self.collectionView reloadSections:indexSet];

Ответ 5

Ответ @Katrin помог много, но я смог добиться еще лучших результатов, добавив еще одну строку:

collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutSubviews() // <-- here it is :)

Теперь я не могу сказать, могу ли я воспроизвести крах с этой строкой или нет, но я думаю, что есть один... Итак, еще не серебряная пуля, но что-то.

Ответ 6

Вызов invalidateLayout не предотвратил сбой в моем случае. (Это работало, если количество элементов в представлении коллекции увеличивалось, но не уменьшалось). Контекст: у меня есть UICollectionView внутри UITableViewCell, и когда ячейка таблицы используется повторно, я сбрасываю делегатов collectionView. Я исправил проблему не путем аннулирования кэша, а путем РЕКРЕАЦИИ объекта макета каждый раз, когда я сбрасывал делегат, затем вызывал reloadData():

foo.dataSource = newDataSource
foo.delegate = newDelegate
foo.fixLayoutBug()
foo.reloadData()

func fixLayoutBug() {
    let oldLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let newLayout = UICollectionViewFlowLayout()
    newLayout.estimatedItemSize = oldLayout.estimatedItemSize
    newLayout.footerReferenceSize = oldLayout.footerReferenceSize
    newLayout.headerReferenceSize = oldLayout.headerReferenceSize
    newLayout.itemSize = oldLayout.itemSize
    newLayout.minimumInteritemSpacing = oldLayout.minimumInteritemSpacing
    newLayout.minimumLineSpacing = oldLayout.minimumLineSpacing
    newLayout.scrollDirection = oldLayout.scrollDirection
    newLayout.sectionFootersPinToVisibleBounds = oldLayout.sectionFootersPinToVisibleBounds
    newLayout.sectionHeadersPinToVisibleBounds = oldLayout.sectionHeadersPinToVisibleBounds
    newLayout.sectionInset = oldLayout.sectionInset
    newLayout.sectionInsetReference = oldLayout.sectionInsetReference
    collectionViewLayout = newLayout
}

Ответ 7

Он не лучший для reloadData каждый раз (вы должны использовать insertItems и deleteItems и даже reloadSections). Но... после того, как я сказал, что в некоторых случаях это действительно так, вы действительно можете это сделать:

collectionView.dataSource = nil

collectionView.delegate = nil

/*... All changes here. ... */

collectionView.dataSource = self

collectionView.delegate = self

collectionView.reloadData()

Ответ 8

В моем случае я менял константу NSlayoutConstraint

self.collectionViewContacts.collectionViewLayout.invalidateLayout()

            UIView.animate(withDuration: 0.3) {
                self.view.layoutIfNeeded()
            }


            self.collectionViewContacts.reloadData()

решил проблему

Ответ 9

Сброс макета UICollectionView решил проблему для меня:

let layout = UICollectionViewFlowLayout()
collectionView?.collectionViewLayout = layout

Ответ 10

У меня была проблема с использованием RxDataSources с RxCollectionViewSectionedAnimatedDataSource и я решил ее, объединив два ответа. Я должен сделать invalidateLayout() активным, когда перезагрузил мою коллекцию:

    ...
    .asDriver(onErrorJustReturn: [])
    .do(onNext: { [weak self] _ in
        DispatchQueue.main.async {
            self?.collectionViewLayout.invalidateLayout()
            self?.collectionView.layoutSubviews()
        }
    }).drive(collectionView.rx.items(dataSource: collectionController.dataSource))
    .disposed(by: disposeBag)

и очистить массив cache в методе layout prepare(). Вот код:

override func prepare() {
    super.prepare()
    cache.removeAll()
    guard let collectionView = collectionView,
        collectionView.numberOfSections > 0,
        let delegate = delegate else {
        return
    }
    ...

Ответ 11

Если вы хотите сохранить свою позицию прокрутки и исправить крах, вы можете использовать это:

collectionView.reloadData()
let context = collectionView.collectionViewLayout.invalidationContext(forBoundsChange: collectionView.bounds)
context.contentOffsetAdjustment = CGPoint.zero
collectionView.collectionViewLayout.invalidateLayout(with: context)

Ответ 12

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

self.filtersCollectionView.reloadData()
if isNeedToUpdateConstraint {
    filtersCollectionViewHeightLayoutConstraint.constant = updatedHeight
    UIView.animate(withDuration: 0.1, animations: {
        self.view.setNeedsLayout()
    }, completion: { completion in
        if completion {
            self.filtersCollectionView.collectionViewLayout.invalidateLayout()
        }
    })
}

Ответ 13

У меня тоже была эта проблема. В моем случае к представлению коллекции применялся пользовательский UICollectionViewLayout, и проблема заключалась в том, что у него были устаревшие данные для количества ячеек, которые должны отображаться. Это определенно то, что вы можете проверить, когда увидите UICollectionView received layout attributes for a cell with an index path that does not exist

Ответ 14

Мне удалось решить эту проблему путем воссоздания collectionViewLayout перед вызовом reloadData()

Вероятно, проблема была связана с тем, что у меня был отдельный экземпляр для dataSource (т. dataSource Не контроллера представления, содержащего collectionView), и при обмене источником данных могло dataSource сбой.

Ответ 15

Это также случилось со мной, но это произошло потому, что мой UICollectionViewDataSource изменился, и я не вызвал -[UICollectionView reloadData]. В моем случае у меня была следующая структура данных:

struct Bar { let name: String }
struct Foo { let name: String; let bars: [Bar] }

У меня было два UICollectionView сек: один для Foo и один для Bar s. В моем -collectionView:didSelectItemAtIndexPath: меня было следующее:

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  if (collectionView == self.fooCollectionView) {
    self.selectedFoo = self.foos[indexPath.row];
    [self.barCollectionView reloadData];
    self.fooCollectionView.hidden = YES;
    self.barCollectionView.hidden = NO;
  } else if (collectionView == self.barCollectionView) {
    // do some stuff with the selected bar

    self.selectedFoo = nil;
    // This -reloadData call fixed my error. I thought I didn't
    // need it since my UICollectionView was hidden
    [self.barCollectionView reloadData];
    self.barCollectionView.hidden = YES;
    self.fooCollectionView.hidden = NO;
  }
}

Без вызова -reloadData я бы увидел сбой при повороте устройства.

Ответ 16

Потратив два дня времени, приведенный ниже код решил проблему. Перед загрузкой коллекции напишите приведенный ниже код.

let flowLayout = UICollectionViewFlowLayout()
flowLayout.scrollDirection = .horizontal
collecView.collectionViewLayout = flowLayout 
collecView.delegate = self
collecView.dataSource = self
collecView.reloadData()

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

flowLayout.itemSize = CGSize(width: 92, height: 100)

С помощью приведенной выше строки кода вы можете настроить макет ячейки.

Надеюсь, это кому-нибудь поможет.

Ответ 17

В моем случае я менял константу NSLayoutConstraint, ни одно из вышеуказанных решений не помогло мне. спасибо @jeff ayan, чей ответ дал мне подсказку. Я решил проблему с этим кодом

override func prepareForReuse() {
    super.prepareForReuse()
    imagesCollectionHeight.constant = 100 // changing collectionview height constant
    imageContainerWidth.constant = 100  //changing width constant of some view
    self.layoutIfNeeded() // this line did the trick for me
}

Надеюсь, это поможет кому-то

Ответ 18

Следующее сделало это для меня:

[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];

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