11 апреля 2015, 16:31

Медленный скроллинг UICollectionView на iOS 8.3

Вчера обновился до iOS 8.3 и заметил, что одно из моих приложений стало ужасно тормозить при скроллинге UICollectionView. Лэйаут там наподобие CoverFlow, в ячейках используется Auto Layout, но на iOS 8.2 скроллинг работал быстро.

Запустил профайлер. Оказалось, что в самом тяжёлом стэке вызовов целых 20% времени съедает метод [UICollectionReusableView _preferredLayoutAttributesFittingAttributes:]:

Начал искать, были ли у кого похожие проблемы, и наткнулся на обсуждение в Гитхабе. У них тоже тормозил скроллинг, причём на более ранних версиях iOS, и кто-то предложил добавить такой код в дочерний класс UICollectionViewCell:

- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    return layoutAttributes;
}

Вставил этот код в свою ячейку, и скроллинг снова заработал быстро. Полез смотреть в документацию. Про метод preferredLayoutAttributesFittingAttributes: написано:

The default implementation of this method adjusts the size values to accommodate changes made by a self-sizing cell.

Но мне не нужен был self-sizing. Стал искать дальше, почему начал вызываться метод, пересчитывающий атрибуты лэйаута. В UICollectionViewFlowLayout.h нашёл поле estimatedItemSize:

// defaults to CGSizeZero - setting a non-zero size enables cells 
// that self-size via -preferredLayoutAttributesFittingAttributes:
@property (nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0);

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

Получается, что в iOS 8.3 ребята из Apple изменили логику self-sizing для ячеек UICollectionView. В любом случае, согласно документации, этот метод не должен вызываться. Если вдруг у вас тоже начал тормозить скроллинг, попробуйте этот фикс :-)

Поделиться
Запинить