UICollectionView flowLayout не обертывание ячеек правильно
У меня есть UICollectionView
с FLowLayout. Он будет работать так, как я ожидаю большую часть времени, но время от времени одна из ячеек не оборачивается должным образом. Например, ячейка, которая должна быть включена в первом "столбце" третьей строки, если фактически заканчивается во второй строке, и есть только пустое пространство, где оно должно быть (см. диаграмму ниже). Все, что вы можете видеть из этой ячейки румян, - это левая сторона (остальная часть отрезана), и место, где она должна быть, пусто.
Это не происходят последовательно; это не всегда одна и та же строка. Как только это произойдет, я могу прокрутить вверх, а затем обратно, и ячейка будет исправлена. Или, когда я нажимаю на ячейку (что приводит меня к следующему виду с помощью нажатия), а затем поп назад, я увижу ячейку в неправильном положении, а затем она перейдет в правильное положение.
скорость прокрутки, кажется, делает его легче воспроизвести проблему. Когда я медленно прокручиваю, я все еще вижу ячейку в неправильном положении время от времени, но тогда он сразу же перейдет в правильное положение.
проблема началась, когда я добавил разделы вставки. Раньше у меня были ячейки почти заподлицо с границами коллекции (мало или нет вставок), и я не заметил проблемы. Но это означало, что справа и слева от представления коллекции было пусто. Т. е. не удалось прокрутить. Кроме того, полоса прокрутки не была заподлицо справа.
Я могу сделать так, чтобы проблема возникла как на симуляторе, так и на iPad 3.
Я думаю, что проблема происходит из - за левой и правой части вставки... Но если значение неверно, то я ожидаю, что поведение будет последовательным. Интересно, может ли это быть ошибка с Apple? Или, возможно, это связано с наращиванием вставок или чем-то подобным.
последующие: я использую этот ответ ниже ником уже более 2 лет без проблем (в случае, если людям интересно, есть ли никаких дыр в этом ответе-я пока не нашел). Молодец Ник.
11 ответов:
существует ошибка в реализации uicollectionviewflowlayout layoutAttributesForElementsInRect, которая заставляет его возвращать два объекта атрибутов для одной ячейки в некоторых случаях, связанных с вставками разделов. Один из возвращенных объектов атрибута является недопустимым (за пределами представления коллекции), а другой-допустимым. Ниже приведен подкласс UICollectionViewFlowLayout, который устраняет проблему, исключая ячейки за пределами границ представления коллекции.
// NDCollectionViewFlowLayout.h @interface NDCollectionViewFlowLayout : UICollectionViewFlowLayout @end // NDCollectionViewFlowLayout.m #import "NDCollectionViewFlowLayout.h" @implementation NDCollectionViewFlowLayout - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; NSMutableArray *newAttributes = [NSMutableArray arrayWithCapacity:attributes.count]; for (UICollectionViewLayoutAttributes *attribute in attributes) { if ((attribute.frame.origin.x + attribute.frame.size.width <= self.collectionViewContentSize.width) && (attribute.frame.origin.y + attribute.frame.size.height <= self.collectionViewContentSize.height)) { [newAttributes addObject:attribute]; } } return newAttributes; } @end
посмотреть этой.
другие ответы предполагают возврат да из shouldinvalidatelayoutforboundsange, но это вызывает ненужные перерасчеты и даже не полностью решает проблему.
мое решение полностью решает ошибку и не должно вызывать никаких проблем, когда Apple устраняет первопричину.
я обнаружил подобные проблемы в моем приложении iPhone. Поиск на форуме Apple dev принес мне это подходящее решение, которое сработало в моем случае и, вероятно, в вашем случае тоже:
подкласс
UICollectionViewFlowLayout
и заменитьshouldInvalidateLayoutForBoundsChange
вернутьсяYES
.//.h @interface MainLayout : UICollectionViewFlowLayout @end
и
//.m #import "MainLayout.h" @implementation MainLayout -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } @end
поместите это в viewController, которому принадлежит представление коллекции
- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; [self.collectionView.collectionViewLayout invalidateLayout]; }
быстрая версия ответа Ника Снайдера:
class NDCollectionViewFlowLayout : UICollectionViewFlowLayout { override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let attributes = super.layoutAttributesForElements(in: rect) let contentSize = collectionViewContentSize return attributes?.filter { .frame.maxX <= contentSize.width && .frame.maxY < contentSize.height } } }
у меня была эта проблема, а также для базового макета gridview с вставками для полей. Ограниченная отладка, которую я сделал на данный момент, реализует
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
в моем подклассе UICollectionViewFlowLayout и путем регистрации того, что возвращает реализация суперкласса, что четко показывает проблему.- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attrsList = [super layoutAttributesForElementsInRect:rect]; for (UICollectionViewLayoutAttributes *attrs in attrsList) { NSLog(@"%f %f", attrs.frame.origin.x, attrs.frame.origin.y); } return attrsList; }
введя
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
Я также вижу, что он, кажется, возвращает неправильные значения для itemIndexPath.item = = 30, что является фактором 10 из числа ячеек моего gridview в строке, а не конечно, если это имеет отношение.- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath { UICollectionViewLayoutAttributes *attrs = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath]; NSLog(@"initialAttrs: %f %f atIndexPath: %d", attrs.frame.origin.x, attrs.frame.origin.y, itemIndexPath.item); return attrs; }
С нехваткой времени для дополнительной отладки, обходной путь, который я сделал на данный момент, уменьшает ширину моих collectionviews с количеством, равным левому и правому краю. У меня есть заголовок, который все еще нуждается в полной ширине, поэтому я установил clipsToBounds = NO на моем collectionview, а затем также удалил левые и правые вставки на нем, похоже, работает. Для того, чтобы вид заголовка оставался на месте, вам нужно реализовать смещение кадра и размер в методах макета, которые ставится задача вернуть layoutAttributes для представления заголовка.
Я добавил отчет об ошибке в Apple. То, что работает для меня, - это установить нижний sectionInset на значение меньше, чем верхняя вставка.
я испытывал ту же проблему с истощением ячеек на iPhone, используя
UICollectionViewFlowLayout
и поэтому я был рад найти ваш пост. Я знаю, что у вас возникли проблемы на iPad, но я отправляю это, потому что я думаю, что это общая проблема сUICollectionView
. Так вот что я узнал.Я могу подтвердить, что
sectionInset
имеет отношение к этой проблеме. Кроме того, чтоheaderReferenceSize
также имеет влияние ли клетка истощена или нет. (Это имеет смысл, так как это необходимо для расчета происхождение.)к сожалению, даже разные размеры экрана должны быть приняты во внимание. Когда я играл со значениями этих двух свойств, я испытал, что определенная конфигурация работала либо на обоих (3.5" и 4"), ни на одном, либо только на одном из размеров экрана. Обычно ни один из них. (Это также имеет смысл, так как границы
UICollectionView
изменения, поэтому я не испытывал никакого несоответствия между сетчаткой и не-сетчаткой.)Я закончил установку
sectionInset
иheaderReferenceSize
в зависимости от размера экрана. Я попробовал около 50 комбинаций, пока не нашел значения, при которых проблема больше не возникала, и макет был визуально приемлемым. Очень трудно найти значения, которые работают на обоих размерах экрана.Итак, подводя итог, я просто могу порекомендовать вам поиграть со значениями, проверить их на разных размерах экрана и надеяться, что Apple исправит эту проблему.
Я только что столкнулся с аналогичной проблемой с ячейками, исчезающими после прокрутки UICollectionView на iOS 10 (нет проблем на iOS 6-9).
подкласс UICollectionViewFlowLayout и переопределение метода layoutAttributesForElementsInRect: не работает в моем случае.
решение было достаточно простым. В настоящее время я использую экземпляр UICollectionViewFlowLayout и set и itemSize и estimatedItemSize (я не использовал estimatedItemSize раньше) и установить его на некоторый ненулевой размер. Фактический размер вычисляется в collectionView:layout: sizeForItemAtIndexPath: метод.
кроме того, я удалил вызов метода invalidateLayout из layoutSubviews, чтобы избежать ненужных перезагрузок.
Я только что испытал подобную проблему, но нашел совсем другое решение.
Я использую пользовательскую реализацию UICollectionViewFlowLayout с горизонтальной прокруткой. Я также создаю пользовательские местоположения кадров для каждой ячейки.
проблема, с которой я столкнулся, заключалась в том, что [super layoutAttributesForElementsInRect:rect] на самом деле не возвращал все UICollectionViewLayoutAttributes, которые должны отображаться на экране. На призывы к [собственной личности.collectionView reloadData] некоторые из ячеек внезапно будут установлены в скрытый.
в итоге я создал NSMutableDictionary, который кэшировал все атрибуты UICollectionViewLayoutAttributes, которые я видел до сих пор, а затем включал любые элементы, которые, как я знаю, должны отображаться.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray * originAttrs = [super layoutAttributesForElementsInRect:rect]; NSMutableArray * attrs = [NSMutableArray array]; CGSize calculatedSize = [self calculatedItemSize]; [originAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * attr, NSUInteger idx, BOOL *stop) { NSIndexPath * idxPath = attr.indexPath; CGRect itemFrame = [self frameForItemAtIndexPath:idxPath]; if (CGRectIntersectsRect(itemFrame, rect)) { attr = [self layoutAttributesForItemAtIndexPath:idxPath]; [self.savedAttributesDict addAttribute:attr]; } }]; // We have to do this because there is a bug in the collection view where it won't correctly return all of the on screen cells. [self.savedAttributesDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSArray * cachedAttributes, BOOL *stop) { CGFloat columnX = [key floatValue]; CGFloat leftExtreme = columnX; // This is the left edge of the element (I'm using horizontal scrolling) CGFloat rightExtreme = columnX + calculatedSize.width; // This is the right edge of the element (I'm using horizontal scrolling) if (leftExtreme <= (rect.origin.x + rect.size.width) || rightExtreme >= rect.origin.x) { for (UICollectionViewLayoutAttributes * attr in cachedAttributes) { [attrs addObject:attr]; } } }]; return attrs; }
вот категория для NSMutableDictionary, что UICollectionViewLayoutAttributes сохраняются правильно.
#import "NSMutableDictionary+CDBCollectionViewAttributesCache.h" @implementation NSMutableDictionary (CDBCollectionViewAttributesCache) - (void)addAttribute:(UICollectionViewLayoutAttributes*)attribute { NSString *key = [self keyForAttribute:attribute]; if (key) { if (![self objectForKey:key]) { NSMutableArray *array = [NSMutableArray new]; [array addObject:attribute]; [self setObject:array forKey:key]; } else { __block BOOL alreadyExists = NO; NSMutableArray *array = [self objectForKey:key]; [array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes *existingAttr, NSUInteger idx, BOOL *stop) { if ([existingAttr.indexPath compare:attribute.indexPath] == NSOrderedSame) { alreadyExists = YES; *stop = YES; } }]; if (!alreadyExists) { [array addObject:attribute]; } } } else { DDLogError(@"%@", [CDKError errorWithMessage:[NSString stringWithFormat:@"Invalid UICollectionVeiwLayoutAttributes passed to category extension"] code:CDKErrorInvalidParams]); } } - (NSArray*)attributesForColumn:(NSUInteger)column { return [self objectForKey:[NSString stringWithFormat:@"%ld", column]]; } - (void)removeAttributesForColumn:(NSUInteger)column { [self removeObjectForKey:[NSString stringWithFormat:@"%ld", column]]; } - (NSString*)keyForAttribute:(UICollectionViewLayoutAttributes*)attribute { if (attribute) { NSInteger column = (NSInteger)attribute.frame.origin.x; return [NSString stringWithFormat:@"%ld", column]; } return nil; } @end
приведенные выше ответы не работают для меня, но после загрузки изображений, Я заменил
[self.yourCollectionView reloadData]
С
[self.yourCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
чтобы обновить и он может показать все ячейки правильно, вы можете попробовать его.
это может быть немного поздно, но убедитесь, что вы устанавливаете ваши атрибуты в
prepare()
Если это возможно.моя проблема заключалась в том, что ячейки выкладывались, а затем получали обновление в
layoutAttributesForElements
. Это привело к эффекту мерцания, когда новые клетки появились в поле зрения.перемещая всю логику атрибутов в
prepare
, а затем установить их вUICollectionViewCell.apply()
он устранил мерцание и создал отображение гладкой ячейки масла