2013-09-30 2 views
41

Кто-нибудь знает, почему contentSize не обновляется сразу после перезагрузкиДата вызывается в UICollectionView?contentSize не обновляется после reloadData вызывается в UICollectionView

Если вам нужно знать, contentSize лучшие работы вокруг я нашел следующее:

[_collectionView reloadData]; 

double delayInSeconds = 0.0001; 
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void) 
    { 
     // TODO: Whatever it is you want to do now that you know the contentSize. 
    }); 

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

Есть ли у кого-нибудь другие обходные пути или знания о том, почему это происходит? Я обсуждаю отправку радара, потому что я не могу понять, почему они не могут вычислить contentSize в том же цикле запуска. Вот как UITableView работал для всей своей реализации.

EDIT: этот вопрос используется для ссылки на метод setContentOffset внутри блока, потому что я хочу прокрутить представление коллекции в моем приложении. Я удалил вызов метода, потому что ответы людей были сосредоточены на том, почему я не использовал scrollToItemAtIndexPath внутри того, почему contentSize не обновляется.

+1

Я думаю, что более подходящим способом было бы использовать scrollToItemAtIndexPath: atScrollPosition: анимированные: , Что касается того, почему размер содержимого не обновляется, я могу только предположить его оптимизацию, чтобы предотвратить либо вызов потенциально многочисленных и дорогостоящих динамических вычислений размера, либо возврат неточной информации, а расширение, выполняющее ненужную обработку и работу. – Matt

+0

В этом приложении я действительно использую это. Для простоты я решил упомянуть setContentOffset. Это хороший момент, когда reloadData, вызываемый несколько раз, может вызвать дорогостоящие вычисления. Слишком плохо, нет метода reloadData с обработчиком завершения. –

ответ

61

Чтобы получить размер контента после перезагрузки, попробуйте вызвать collectionViewContentSize объекта размещения. Меня устраивает.

+3

Сначала я был настроен скептически, но эй тоже работал для меня :) – Zhang

+0

Это похоже на вызовы reloadData, которые не сразу запускают представление коллекции, чтобы изменить его свойства рендеринга, но, как и Clement, вы можете перейти непосредственно к объекту макета, чтобы получить эту информацию немедленно. –

+0

SWIFT 3: let contentSize = self.myCollectionView.collectionViewLayout.collectionViewContentSize – ripegooseberry

5

Edit: Я только что проверил это и в самом деле, когда данные изменения, мое первоначальное решение будет врезаться со следующим:

«Invalid обновление: недопустимое количество элементов в разделе 0. Число элементов, содержащихся в существующем разделе после обновления (7) должно быть равно количеству элементов, содержащихся в этом разделе, перед обновлением (100) плюс или минус количество элементов, вставленных или удаленных из этого раздела (0 вставлено, 0 удалено) и плюс или минус количество элементов, перемещенных в или из этого раздела (0 перемещено, 0 выведено). "

Правильный способ справиться с этим - рассчитать вставки, удаления и перемещения всякий раз, когда источник данных изменяется, и использовать функции executeBatchUpdates вокруг них, когда это происходит. Например, если два элемента добавляется в конец массива, который является источником данных, это будет код:

NSArray *indexPaths = @[indexPath1, indexPath2]; 
[self.collectionView performBatchUpdates:^() 
{ 
    [self.collectionView insertItemsAtIndexPaths:indexPaths]; 
} completion:^(BOOL finished) { 
    // TODO: Whatever it is you want to do now that you know the contentSize. 
}]; 

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

Попробуйте выполнитьBatchUpdates: завершение: по UICollectionView. У вас должен быть доступ к обновленным свойствам в блоке завершения. Это будет выглядеть следующим образом:

[self.collectionView reloadData]; 
[self.collectionView performBatchUpdates:^() 
{ 

} completion:^(BOOL finished) { 
    // TODO: Whatever it is you want to do now that you know the contentSize. 
}]; 
+1

Это похоже на твердое решение. Изменения будут оживлять, что не то, что я хочу, но я уверен, что должен быть способ убить анимацию. –

+0

Вы можете переопределить атрибуты исходного макета в подклассе макета, используя initialLayoutAttributesForAppearingItemAtIndexPath :. Установите рамку в кадре меняющихся ячеек. – brodney

+1

Я уверен, что это сработает, вы должны вывести reloadData из блока executeBatch. –

-1

Вызов prepareLayout первым, и тогда вы получите правильный contentSize:

[self.collectionView.collectionViewLayout prepareLayout]; 
+2

Неверно. Реализация по умолчанию ничего не делает, как указано Apple: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UICollectionViewLayout_class/index.html#//apple_ref/occ/instm/UICollectionViewLayout/prepareLayout –

+1

согласен с @ CanPoyrazoğlu –

+0

Это интересно - у меня есть случай, в котором вызов invalidateLayout, за которым следует reloadData, не вызывает вызов sizeForItem: call. Тем не менее, вызов prepareLayout непосредственно между invalidate и reload приводит к тому, что размеры будут пересчитываться во время перезагрузки, как и следовало ожидать ... Документы говорят, что реализация пуста, но я не уверен, что полностью верю в это ... – tyler

10

Это работало для меня:

[self.collectionView.collectionViewLayout invalidateLayout]; 
[self.collectionView.collectionViewLayout prepareLayout]; 
+1

Работает для меня с комбинацией layoutIfNeeded thats. – KiranJasvanee

Смежные вопросы