UICollectionView flowLayout没有正确包装单元格

lin*_*fox 77 ios ios6 uicollectionview uicollectionviewcell

我有UICollectionView一个FLowLayout.它会像我期望的那样工作,但是偶尔会有一个单元格无法正确包装.例如,如果实际上在第二行中尾随,则应该在第三行的第一个"列"中打开的单元格,并且它应该是一个空的空间(参见下图).所有你能看到的这个胭脂细胞是左手边(其余的被切掉),它应该是空的.

这不会持续发生; 它并不总是同一行.一旦它发生了,我可以向上滚动然后再回来,单元格将自行修复.或者,当我按下单元格(通过按钮将我带到下一个视图)然后弹回时,我将看到单元格位于错误的位置,然后它将跳转到正确的位置.

滚动速度似乎可以更容易地重现问题.当我慢慢滚动时,我仍然可以偶尔看到单元格处于错误的位置,但是它会立即跳到正确的位置.

当我添加插件插件时,问题就开始了.以前,我的细胞几乎与集合边界齐平(很少或没有插入),我没有注意到这个问题.但这意味着集合视图的右侧和左侧是空的.即,无法滚动.此外,滚动条没有向右冲洗.

我可以在模拟器和iPad 3上解决问题.

我猜这个问题正在发生,因为左右部分插入...但如果值是错误的,那么我希望行为是一致的.我想知道这可能是Apple的错误吗?或许这可能是由于插入物的堆积或类似的东西.

问题和设置的例证


跟进:我一直在使用尼克的这个答案超过2年,现在没有问题(如果人们想知道答案中是否有任何漏洞 - 我还没有找到任何漏洞).做得好尼克.

Nic*_*der 94

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
Run Code Online (Sandbox Code Playgroud)

看到这个.

其他答案建议从shouldInvalidateLayoutForBoundsChange返回YES,但这会导致不必要的重新计算,甚至不能完全解决问题.

我的解决方案完全解决了这个错误,并且在Apple修复根本原因时不应该导致任何问题.

  • 仅供参考,这个错误也会出现水平滚动.用y替换x和用高度替换宽度使得这个补丁工作. (5认同)
  • @Rpranata从iOS 7.1开始,这个bug还没有修复.叹. (2认同)
  • 也许我正在使用这个错误,但在Swift的iOS 8.3上,这会导致右侧的子视图过去根本不会出现.还有谁? (2认同)

xxt*_*axx 7

我在iPhone应用程序中发现了类似的问题.搜索Apple开发论坛给我带来了这个合适的解决方案,这个解决方案适用于我的情况,也可能在你的情况下:

子类UICollectionViewFlowLayout和覆盖shouldInvalidateLayoutForBoundsChange返回YES.

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end
Run Code Online (Sandbox Code Playgroud)

//.m
#import "MainLayout.h"
@implementation MainLayout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
@end
Run Code Online (Sandbox Code Playgroud)


Pet*_*isu 7

将它放入拥有集合视图的viewController中

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}
Run Code Online (Sandbox Code Playgroud)

  • 我的问题是细胞完全消失了.此解决方案有所帮助 - 但这会导致不必要的重新加载.现在还在工作.. thx! (3认同)

Pat*_*pel 7

一个Swift版本的Nick Snyder的答案:

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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这完全使 CollectionViewCell 消失...还有其他可能的解决方案吗? (2认同)

mon*_*ker 5

我也有这个问题,对于带有插入边距的基本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;
}
Run Code Online (Sandbox Code Playgroud)

通过实现- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(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;
}
Run Code Online (Sandbox Code Playgroud)

由于没有时间进行更多调试,我现在所做的解决方法是减少我的collectionviews宽度,其数量等于左右边距.我有一个仍然需要全宽度的标题,所以我在我的集​​合视图上设置了clipsToBounds = NO,然后还删除了左边和右边的插图,似乎工作.为了使标题视图保持原位,您需要在布局方法中实现帧移位和大小调整,这些布局方法的任务是返回标题视图的layoutAttributes.