UICollectionView не удаляет старые ячейки после прокрутки
У меня есть UICollectionView с сеткой изображений. Когда вы нажмете на один, он откроет сетку и покажет subview с некоторыми деталями. Вот так:
![Supposed to look like this]()
Я открываю сетку в своем UICollectionViewLayout, настраивая UICollectionViewLayoutAttributes и устанавливая перевод в свойстве transform3D для всех ячеек ниже текущей строки выбранного элемента. Это работает очень хорошо, и это намного лучшая анимация и более простой подход, чем моя первая попытка вставить другую ячейку в сетку, которая отличается от других.
В любом случае... он работает большую часть времени, но затем, после продолжения использования, я вижу старые изображения в представлении коллекции. Они похожи на призрачные клетки. Я не могу нажимать на них, это похоже на то, что они не были удалены из представления коллекции должным образом, и они сидят поверх ячеек, предотвращая краны и просто раздражая. Вот так:
![Problem looks like this]()
Любые идеи, почему эти клетки делают это?
EDIT:
Я бы хотел добавить, я думаю, что это происходит только тогда, когда я быстро просматриваю представление коллекции. Я написал свою собственную замену UICollectionViewFlowLayout для проверки, если это все еще происходит. Оно делает.
ИЗМЕНИТЬ 2:
3D-преобразования или макет не имеют к этому никакого отношения. Это должно быть ошибкой в UICollectionView. Я могу использовать, просто прокручивая очень быстро, позволяя зайти в тупик, а затем запросить представления, которые находятся на экране. Часто бывает вдвое больше клеток, но они скрыты, поскольку они сложены друг на друга. Моя реализация выше показывает их из-за перевода, который я делаю.
Это также может повредить производительность.
См. мой ответ для решения.
Ответы
Ответ 1
Мое второе редактирование моего вопроса объясняет, почему это происходит, и вот мое обходное решение. Это не пуленепробиваемый, но он работает в моем случае, и если вы испытываете нечто похожее, вы можете настроить мое решение:
- (void) removeNaughtyLingeringCells {
// 1. Find the visible cells
NSArray *visibleCells = self.collectionView.visibleCells;
//NSLog(@"We have %i visible cells", visibleCells.count);
// 2. Find the visible rect of the collection view on screen now
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
//NSLog(@"Rect %@", NSStringFromCGRect(visibleRect));
// 3. Find the subviews that shouldn't be there and remove them
//NSLog(@"We have %i subviews", self.collectionView.subviews.count);
for (UIView *aView in [self.collectionView subviews]) {
if ([aView isKindOfClass:UICollectionViewCell.class]) {
CGPoint origin = aView.frame.origin;
if(CGRectContainsPoint(visibleRect, origin)) {
if (![visibleCells containsObject:aView]) {
[aView removeFromSuperview];
}
}
}
}
//NSLog(@"%i views shouldn't be there", viewsShouldntBeThere.count);
// 4. Refresh the collection view display
[self.collectionView setNeedsDisplay];
}
и
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self removeNaughtyLingeringCells];
}
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self removeNaughtyLingeringCells];
}
Ответ 2
Быстрый дальнейший комментарий к bandejapaisa's: только под iOS 6 я обнаружил, что UICollectionView
также имел привычку создавать анимированные переходы. Исходные ячейки останутся там, где они были, будут сделаны копии, а затем копии будут анимированы. Обычно поверх оригиналов, но не всегда. Таким образом, простой тест оценок был недостаточным.
Поэтому я написал собственный подкласс UICollectionView
, который выполняет следующие действия:
- (void)didAddSubview:(UIView *)subview
{
[super didAddSubview:subview];
//
// iOS 6 contains a bug whereby it fails to remove subviews, ever as far as I can make out.
// This is a workaround for that. So, if this is iOS 6...
//
if(![UIViewController instancesRespondToSelector:@selector(automaticallyAdjustsScrollViewInsets)])
{
// ... then we'll want to wait until visibleCells has definitely been updated ...
dispatch_async(dispatch_get_main_queue(),
^{
// ... then we'll manually remove anything that a sub of UICollectionViewCell
// and isn't currently listed as a visible cell
NSArray *visibleCells = self.visibleCells;
for(UIView *view in self.subviews)
{
if([view isKindOfClass:[UICollectionViewCell class]] && ![visibleCells containsObject:view])
[view removeFromSuperview];
}
});
}
}
Очевидно, стыдно, что "этот тест iOS 6" не может быть немного более прямым, но он скрыт в категории в моем фактическом коде.
Ответ 3
Быстрая версия расширения UICollectionView для ответа bandejapaisa:
extension UICollectionView {
func removeNaughtyLingeringCells() {
// 1. Find the visible cells
let visibleCells = self.visibleCells()
//NSLog("We have %i visible cells", visibleCells.count)
// 2. Find the visible rect of the collection view on screen now
let visibleRect = CGRectOffset(bounds, contentOffset.x, contentOffset.y)
//NSLog("Rect %@", NSStringFromCGRect(visibleRect))
// 3. Find the subviews that shouldn't be there and remove them
//NSLog("We have %i subviews", subviews.count)
for aView in subviews {
if let aCollectionViewCell = aView as? UICollectionViewCell {
let origin = aView.frame.origin
if (CGRectContainsPoint(visibleRect, origin)) {
if (!visibleCells.contains(aCollectionViewCell)) {
aView.removeFromSuperview()
}
}
}
}
// 4. Refresh the collection view display
setNeedsDisplay()
}
}