ios6 - UICollectionView flowLayout没有正确包装单元格(iOS)




uicollectionviewcell (10)

我有一个UICollectionView的UICollectionView。 它会按我所预期的大部分时间工作,但是现在和其中一个单元格不能正确包装。 例如,如果实际上在第二行中出现尾随,并且只有一个应该在其中的空白空间(请参见下图),则该单元应位于第三行的第一个“列”中。 你所看到的这个胭脂细胞就是左手边(其余的被切除),它应该是空的。

这不会一致发生; 它并不总是相同的行。 一旦发生,我可以向上滚动,然后返回,单元格将自动修复。 或者,当我按下单元格(通过推动将我带到下一个视图)然后弹出时,我会看到单元格位置不正确,然后它会跳到正确的位置。

滚动速度似乎更容易重现问题。 当我慢慢地滚动时,我仍然可以看到单元格在错误的位置,然后马上跳到正确的位置。

当我添加节插图时,问题就开始了。 以前,我的单元格几乎与收集边界齐平(很少或没有插入),我没有注意到这个问题。 但是这意味着收集视图的右侧和左侧是空的。 即,不能滚动。 而且,滚动条并没有朝右齐平。

我可以在模拟器和iPad 3上发生问题。

我猜这个问题正在发生,因为左侧和右侧部分的插入...但是,如果价值是错误的,那么我会期望行为是一致的。 我想知道这是否会成为Apple的一个漏洞? 或者,这可能是由于内嵌物或类似物的积聚造成的......

任何解决方案/解决方法,赞赏。

后续行动 :我已经在下面用Nick的这个答案超过6个 月,现在12个月 2年没有问题(如果人们想知道这个答案是否有漏洞 - 我还没有找到任何答案)。 尼克做得好。


Answers

我刚刚遇到类似的问题, iOS 10中的 UICollectionView滚动后单元消失(iOS 6-9没有问题)。

UICollectionViewFlowLayout的子类化和重写方法layoutAttributesForElementsInRect:在我的情况下不起作用。

解决方案很简单。 目前我使用UICollectionViewFlowLayout的一个实例并设置itemSize和estimatedItemSize (我之前没有使用estimatedItemSize)并将其设置为某个非零大小。 实际大小在collectionView:layout:sizeForItemAtIndexPath:方法中进行计算。

此外,我已从layoutSubviews中移除了invalidateLayout方法的调用,以避免不必要的重新加载。


我也遇到了这个问题,以及一个基本的gridview布局,其中包含用于页边空白的insets。 我现在所做的有限的调试是在我的UICollectionViewFlowLayout子类中实现- (NSArray *)layoutAttributesForElementsInRect:(CGRect)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 *)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;
}

由于缺乏更多调试时间,我现在所做的解决方法是减少我的收藏视图宽度,数量等于左右边距。 我有一个头仍然需要全宽,所以我在我的collectionview上设置了clipsToBounds = NO,然后也删除了左边和右边的插图,似乎工作。 为了使标题视图保持原状,您需要在布局方法中实现框架移动和尺寸调整,这些方法的任务是为标题视图返回layoutAttributes。


我在iPhone应用程序中发现了类似的问题。 搜索Apple dev论坛为我带来了这个合适的解决方案,它适用于我的案例,并且可能也适用于您的案例:

子类UICollectionViewFlowLayout并重写shouldInvalidateLayoutForBoundsChange以返回YES

//.h
@interface MainLayout : UICollectionViewFlowLayout
@end

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

我刚刚遇到类似的问题,但发现了一个非常不同的解决方案

我正在使用横​​向滚动的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

在UICollectionViewFlowLayout的layoutAttributesForElementsInRect的实现中存在一个错误,它导致它在某些涉及节插入的情况下返回单个单元的TWO属性对象。 其中一个返回的属性对象是无效的(在集合视图的范围之外),另一个是有效的。 下面是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

看到this

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

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


我在使用UICollectionViewFlowLayout的iPhone上遇到了同样的细胞替换问题,所以我很高兴找到你的帖子。 我知道你在iPad上遇到问题,但是我发布了这个消息,因为我认为这是UICollectionView一个普遍问题。 所以这是我发现的。

我可以确认sectionInset与该问题有关。 除此之外, headerReferenceSize也影响一个单元格是否被取代。 (这是有意义的,因为它是计算原点所需的。)

不幸的是,即使不同的屏幕尺寸也必须考虑在内。 当玩弄这两个属性的值时,我体验到某种配置对两个(3.5“和4”)都没有影响,或者只影响其中一个屏幕大小。 通常他们都不是。 (这也是有道理的,因为UICollectionView的边界发生了变化,所以我没有经历视网膜和非视网膜之间的任何差异。)

我最终根据屏幕大小设置了sectionInsetheaderReferenceSize 。 我尝试了大约50种组合,直到我找到了问题不再发生的值,并且布局在视觉上可以接受。 很难找到适用于两种屏幕尺寸的值。

因此,总结一下,我可以推荐你使用这些值,在不同的屏幕尺寸上检查这些值,并希望Apple能够解决这个问题。


这可能有点晚,但如果可能的话,确保你在prepare()中设置了你的属性。

我的问题是,单元布局,然后获得layoutAttributesForElements更新。 当新单元出现时,这会导致闪烁效应。

通过将所有的属性逻辑移动到prepare ,然后在UICollectionViewCell.apply()设置它们,它消除了闪烁并创建了黄油平滑单元格,显示😊


把它放到拥有集合视图的viewController中

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

上述答案对我来说不起作用,但下载完图像后,我将其替换

[self.yourCollectionView reloadData]

[self.yourCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];

刷新并且可以正确显示所有单元格,您可以尝试它。


例如,在Swift 3中使用Alamofire 4.0.0

(确保你已准备好4.0.0,因为你好像尚未更新你的Alamofire)

Alamofire.upload(multipartFormData: { (multipartFormData) in
        // code
    }, to: URL, encodingCompletion: { (result) in
        // code
    })

要么

Alamofire.upload(multipartFormData: { (multipartFormData) in
        // code
    }, with: URL, encodingCompletion: { (result) in
        // code
    })

所以headers需要通过URL请求传递:

let URL = try! URLRequest(url: "http://example.com", method: .get, headers: headers)




ios ios6 uicollectionview uicollectionviewcell