Ios UICollectionView流程布局未正确包装单元格

Ios UICollectionView流程布局未正确包装单元格,ios,ios6,uicollectionview,uicollectionviewcell,Ios,Ios6,Uicollectionview,Uicollectionviewcell,我有一个带有FLowLayout的UICollectionView。它在大多数情况下都会像我预期的那样工作,但偶尔会有一个细胞包裹不好。例如,如果在第二行中实际尾随,则第三行的第一个“列”中应该打开的单元格,并且该单元格所在的位置只有一个空白(请参见下图)。你所能看到的这个胭脂细胞是左手边(其余部分被切断),它应该是空的地方 这种情况并不总是发生;它并不总是同一行。一旦发生了,我可以向上滚动,然后再往回滚动,电池就会自行修复。或者,当我按下单元格(通过一个按钮将我带到下一个视图)然后弹回来时,我

我有一个带有FLowLayout的
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 }
    }
}