Ios UICollectionView流程布局未正确包装单元格
我有一个带有FLowLayout的Ios UICollectionView流程布局未正确包装单元格,ios,ios6,uicollectionview,uicollectionviewcell,Ios,Ios6,Uicollectionview,Uicollectionviewcell,我有一个带有FLowLayout的UICollectionView。它在大多数情况下都会像我预期的那样工作,但偶尔会有一个细胞包裹不好。例如,如果在第二行中实际尾随,则第三行的第一个“列”中应该打开的单元格,并且该单元格所在的位置只有一个空白(请参见下图)。你所能看到的这个胭脂细胞是左手边(其余部分被切断),它应该是空的地方 这种情况并不总是发生;它并不总是同一行。一旦发生了,我可以向上滚动,然后再往回滚动,电池就会自行修复。或者,当我按下单元格(通过一个按钮将我带到下一个视图)然后弹回来时,我
UICollectionView
。它在大多数情况下都会像我预期的那样工作,但偶尔会有一个细胞包裹不好。例如,如果在第二行中实际尾随,则第三行的第一个“列”中应该打开的单元格,并且该单元格所在的位置只有一个空白(请参见下图)。你所能看到的这个胭脂细胞是左手边(其余部分被切断),它应该是空的地方
这种情况并不总是发生;它并不总是同一行。一旦发生了,我可以向上滚动,然后再往回滚动,电池就会自行修复。或者,当我按下单元格(通过一个按钮将我带到下一个视图)然后弹回来时,我会看到单元格处于错误的位置,然后它会跳到正确的位置
滚动速度似乎更容易重现问题。当我慢慢滚动时,我仍然可以不时地看到单元格处于错误的位置,但它会立即跳转到正确的位置
当我添加部分插入时,问题就开始了。以前,我让单元格几乎与集合边界齐平(很少或没有插入),但我没有注意到这个问题。但这意味着集合视图的右侧和左侧是空的。Ie,无法滚动。此外,滚动条未向右齐平
我可以在模拟器和iPad3上解决这个问题
我想问题的出现是因为左边和右边的插图。。。但是如果值是错误的,那么我希望行为是一致的。我想知道这是否是苹果的一个错误?或者这可能是由于插图或类似的东西的积累
跟进:我已经在Nick的下面使用了2年多,没有出现任何问题(如果人们想知道答案中是否有漏洞,我还没有发现任何漏洞)。干得好,尼克。对于基本的gridview布局,我也遇到了这个问题,它带有用于边距的插图。我目前所做的有限调试是在我的UICollectionViewFlowLayout子类中实现
-(NSArray*)layoutAttributesForElementsInRect:(cRect)rect
,并记录超类实现返回的内容,这清楚地显示了问题
- (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*)InitialLayoutAttributes for AppearinGitemAtIndexPath:(NSIndexPath*)itemIndexPath
,我还可以看到它似乎为itemIndexPath.item==30返回了错误的值,这是gridview每行单元格数的因子10,不确定这是否相关
- (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的宽度减少到与左右边距相等的程度。我有一个页眉仍然需要全宽,所以我在collectionview上设置了clipsToBounds=NO,然后删除了页眉上的左、右插图,看起来效果不错。要使标题视图保持不变,您需要在布局方法中实现帧移动和大小调整,这些方法的任务是返回标题视图的布局属性。我在iPhone上使用
UICollectionViewFlowLayout
时遇到了相同的单元格移动问题,因此我很高兴找到您的帖子。我知道你在iPad上遇到了这个问题,但我之所以发布这个帖子,是因为我认为这是UICollectionView
的一个普遍问题。这就是我发现的
我可以确认部分插图
与该问题相关。除此之外,headerReferenceSize
还影响单元格是否移位。(这是有意义的,因为它是计算原点所必需的。)
不幸的是,甚至不同的屏幕尺寸也必须考虑在内。在处理这两个属性的值时,我体验到某个配置在两个(3.5英寸和4英寸)、无或仅一个屏幕大小上都起作用。通常没有。(这也是有道理的,因为UICollectionView
的边界发生了变化,因此我没有体验到视网膜和非视网膜之间的任何差异。)
最后,我根据屏幕大小设置了部分inset
和headerReferenceSize
。我尝试了大约50种组合,直到找到不再出现问题的值,并且布局在视觉上是可以接受的。很难找到适用于两种屏幕大小的值
总而言之,我可以建议你玩转这些值,在不同的屏幕大小上检查这些值,并希望苹果能解决这个问题。我在我的iPhone应用程序中发现了类似的问题。搜索Apple dev论坛为我带来了这个合适的解决方案,它在我的案例中有效,在您的案例中也可能有效: 子类
UICollectionViewFlowLayout
和重写应为边界设置验证布局
以返回是
//.h
@interface MainLayout : UICollectionViewFlowLayout
@end
及
我给苹果添加了一个bug报告。对我来说,有效的方法是将底部sectionInset设置为小于顶部inset的值。UICollectionViewFlowLayout对LayoutAttributesForElementsRect的实现中存在一个错误,导致它在某些涉及节插入的情况下为单个单元格返回两个属性对象。返回的属性对象之一无效(在集合视图的边界之外),另一个有效。下面是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
//NDCollectionViewFlowLayout.h
@接口NDCollectionViewFlowLayout:UICollectionViewFlowLayout
@结束
//NDCollectionViewFlowLayout.m
#导入“NDCollectionViewFlowLayout.h”
@NDCollectionViewFlowLayout的实现
-(NSArray*)布局属性ForElementsInRect:(CGRect)rect{
NSArray*属性=[s
// 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
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
[self.collectionView.collectionViewLayout invalidateLayout];
}
- (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;
}
#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]];
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 }
}
}