У меня UICollectionView
есть FLowLayout. Большую часть времени он будет работать так, как я ожидал, но время от времени одна из ячеек не переносится должным образом. Например, ячейка, которая должна находиться в первом «столбце» третьей строки, если на самом деле заканчивается во второй строке, и там, где должно быть, есть только пустое место (см. Диаграмму ниже). Все, что вы можете видеть в этой ячейке с румянцами, - это левая сторона (остальная часть обрезана), и место, где она должна быть, пусто.
Это не происходит постоянно; это не всегда один и тот же ряд. Как только это произойдет, я могу прокрутить вверх, а затем назад, и ячейка зафиксируется сама. Или, когда я нажимаю ячейку (что переводит меня к следующему представлению с помощью нажатия), а затем возвращаюсь, я увижу ячейку в неправильном положении, а затем она перейдет в правильное положение.
Скорость прокрутки, кажется, упрощает воспроизведение проблемы. Когда я прокручиваю медленно, я все еще могу видеть ячейку в неправильном положении время от времени, но тогда она сразу же перейдет в правильное положение.
Проблема началась, когда я добавил вставки разделов. Раньше у меня ячейки были почти на одном уровне с границами коллекции (маленькие вставки или их нет), и я не замечал проблемы. Но это означало, что правая и левая часть представления коллекции были пусты. Т.е. прокручивать не мог. Кроме того, полоса прокрутки не смещена вправо.
Я могу сделать проблему как на Симуляторе, так и на iPad 3.
Я предполагаю, что проблема возникает из-за вставок левого и правого разделов ... Но если значение неправильное, я ожидаю, что поведение будет согласованным. Интересно, может ли это быть ошибкой Apple? Или, возможно, это связано с нарастанием вставок или чем-то подобным.
Дальнейшие действия : я использую этот ответ Ника ниже более 2 лет без проблем (на случай, если люди задаются вопросом, есть ли в этом ответе какие-либо дыры - я их еще не нашел). Молодец, Ник.
Поместите это в viewController, которому принадлежит представление коллекции
- (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; [self.collectionView.collectionViewLayout invalidateLayout]; }
источник
invalidateLayout
только один раз.я обнаружил похожие проблемы в своем приложении для iPhone. Поиск на форуме разработчиков Apple дал мне это подходящее решение, которое сработало в моем случае и, вероятно, будет и в вашем случае:
Подкласс
UICollectionViewFlowLayout
и переопределениеshouldInvalidateLayoutForBoundsChange
для возвратаYES
.//.h @interface MainLayout : UICollectionViewFlowLayout @end
и
//.m #import "MainLayout.h" @implementation MainLayout -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } @end
источник
Быстрая версия ответа Ника Снайдера:
class NDCollectionViewFlowLayout : UICollectionViewFlowLayout { override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let attributes = super.layoutAttributesForElements(in: rect) let contentSize = collectionViewContentSize return attributes?.filter { $0.frame.maxX <= contentSize.width && $0.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; }
Из-за нехватки времени на дополнительную отладку обходной путь, который я сделал на данный момент, заключается в уменьшении ширины моего представления коллекций на величину, равную левому и правому полю. У меня есть заголовок, который по-прежнему требует полной ширины, поэтому я установил clipsToBounds = NO в моем обзоре коллекции, а затем также удалил на нем левую и правую вставки, похоже, работает. Чтобы представление заголовка оставалось на месте, вам необходимо реализовать сдвиг кадра и изменение размера в методах макета, которым поручено возвращать атрибуты layoutAttributes для представления заголовка.
источник
Я добавил отчет об ошибке в Apple. Что мне подходит, так это установить для bottom sectionInset значение меньше верхнего inset.
источник
У меня была такая же проблема с заменой ячеек на iPhone с помощью a,
UICollectionViewFlowLayout
и я был рад найти ваш пост. Я знаю, что у вас проблема с iPad, но я публикую это, потому что думаю, что это общая проблема с расширениемUICollectionView
. Итак, вот что я узнал.Я могу подтвердить, что это
sectionInset
имеет отношение к этой проблеме. Кроме того,headerReferenceSize
также влияет, будет ли ячейка перемещена или нет. (Это имеет смысл, поскольку это необходимо для вычисления происхождения.)К сожалению, приходится учитывать даже разные размеры экрана. Играя со значениями этих двух свойств, я обнаружил, что определенная конфигурация работает либо на обоих (3,5 дюйма, либо на 4 дюйма), ни на одном из них, либо только на одном из размеров экрана. Обычно их нет. (Это тоже имеет смысл, поскольку границы
UICollectionView
изменений, поэтому я не испытал несоответствия между сетчаткой и не сетчаткой.)В итоге я установил
sectionInset
и вheaderReferenceSize
зависимости от размера экрана. Я пробовал около 50 комбинаций, пока не нашел значения, при которых проблема больше не возникала и макет был визуально приемлемым. Очень сложно найти значения, которые работают для обоих размеров экрана.Подводя итог, я просто могу порекомендовать вам поиграть со значениями, проверить их на разных размерах экрана и надеяться, что Apple исправит эту проблему.
источник
Я только что столкнулся с аналогичной проблемой с исчезновением ячеек после прокрутки UICollectionView на iOS 10 (нет проблем на iOS 6-9).
Создание подклассов UICollectionViewFlowLayout и переопределение метода layoutAttributesForElementsInRect: в моем случае не работает.
Решение было достаточно простым. В настоящее время я использую экземпляр UICollectionViewFlowLayout и устанавливаю как itemSize, так и EstimatedItemSize (раньше я не использовал ) и устанавливаю его на некоторый ненулевой размер. Фактический размер вычисляется в методе collectionView: layout: sizeForItemAtIndexPath:.
Кроме того, я удалил вызов метода invalidateLayout из layoutSubviews, чтобы избежать ненужных перезагрузок.
источник
У меня возникла аналогичная проблема, но я нашел совсем другое решение.
Я использую пользовательскую реализацию UICollectionViewFlowLayout с горизонтальной прокруткой. Я также создаю пользовательские местоположения фреймов для каждой ячейки.
Проблема, с которой я столкнулся, заключалась в том, что [super layoutAttributesForElementsInRect: rect] фактически не возвращал все атрибуты UICollectionViewLayoutAttributes, которые должны отображаться на экране. При вызове [self.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 reloadSections:[NSIndexSet indexSetWithIndex:0]];
чтобы обновить, и он может правильно отображать все ячейки, вы можете попробовать.
источник
Это может быть немного поздно, но по возможности убедитесь, что вы устанавливаете свои атрибуты
prepare()
.Моя проблема заключалась в том, что ячейки выкладывались, а затем обновлялись
layoutAttributesForElements
. Это приводило к эффекту мерцания при появлении новых ячеек.Переместив всю логику атрибутов
prepare
, а затем установив их вUICollectionViewCell.apply()
ней, мы устранили мерцание и создали масляно-гладкую ячейку отображения 😊источник