collectionView之LeftAlign的bug

23 篇文章 0 订阅
15 篇文章 0 订阅

界面实现如图所示的collectionView

这里写图片描述
即每个cell之间的间距就是定义的最小间距,并且依次向左靠齐。

其实现方法是重写UICollectionViewFlowLayout。

代码如下:
UICollectionViewLeftAlignFlowLayout.h文件

@interface UICollectionViewLeftAlignFlowLayout : UICollectionViewFlowLayout

@end

UICollectionViewLeftAlignFlowLayout.m文件

#import "UICollectionViewLeftAlignFlowLayout.h"
@interface UICollectionViewLayoutAttributes (LeftAligned)

- (void)leftAlignFrameWithSectionInset:(UIEdgeInsets)sectionInset;

@end

@implementation UICollectionViewLayoutAttributes (LeftAligned)

- (void)leftAlignFrameWithSectionInset:(UIEdgeInsets)sectionInset
{
    CGRect frame = self.frame;
    frame.origin.x = sectionInset.left;
    self.frame = frame;
}

@end

@implementation UICollectionViewLeftAlignFlowLayout

- (instancetype)init
{
    if (self = [super init]) {

    }
    return self;
}
//返回整个collectionview中所有cell和view 的属性
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
//    NSArray *attributesArrayReturn = [super layoutAttributesForElementsInRect:rect];
//    NSArray *attributesArrayReturn = [NSArray arrayWithArray:[super layoutAttributesForElementsInRect:rect]];//这两句都会出现控制台的警告信息
    NSArray *attributesArrayReturn = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];

    for (UICollectionViewLayoutAttributes *attributes in attributesArrayReturn) {
        //这代表的是当元素cell 时返回 cell的UICollectionElementCategoryCell
        if (attributes.representedElementKind == nil) {
            UICollectionViewLayoutAttributes *curAttributes = [[self layoutAttributesForItemAtIndexPath:attributes.indexPath] copy];
            attributes.frame = curAttributes.frame;
        }
    }
    return attributesArrayReturn;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes *curAttributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
    UIEdgeInsets sectionInset = [self evaluateSectionInsetForItemAtIndexPath:indexPath];

    BOOL isFirstItemInSection = indexPath.item == 0;
    CGFloat layoutWidth = CGRectGetWidth(self.collectionView.frame) - sectionInset.left - sectionInset.right;

    if (isFirstItemInSection) {
        [curAttributes leftAlignFrameWithSectionInset:sectionInset];
        return curAttributes;
    }

    NSIndexPath *previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item - 1 inSection:indexPath.section];
    UICollectionViewLayoutAttributes *tmpAttributes = [[self layoutAttributesForItemAtIndexPath:previousIndexPath] copy];

    CGRect previousFrame = tmpAttributes.frame;
    CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width;
    CGRect currentFrame = curAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(sectionInset.left,currentFrame.origin.y,layoutWidth,currentFrame.size.height);
    // if the current frame, once left aligned to the left and stretched to the full collection view
    // widht intersects the previous frame then they are on the same line
    BOOL isFirstItemInRow = !CGRectIntersectsRect(previousFrame, strecthedCurrentFrame);

    if (isFirstItemInRow) {
        //确保第一个cell在行的左侧
        [curAttributes leftAlignFrameWithSectionInset:sectionInset];
        return curAttributes;
    }

    CGRect frame = curAttributes.frame;
    frame.origin.x = previousFrameRightPoint + [self evaluateMinimumInteritemSpacingForItemAtIndexPath:indexPath];
    curAttributes.frame = frame;
    return curAttributes;
}
#pragma  mark -  下面是计算的方法,不是重写父类
//行
- (UIEdgeInsets)evaluateSectionInsetForItemAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.collectionView.delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {
        id<UICollectionViewDelegateFlowLayout> delegate = (id<UICollectionViewDelegateFlowLayout>)self.collectionView.delegate;
        return [delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:indexPath.section];
    } else {
        return self.sectionInset;
    }
}
//列
- (CGFloat)evaluateMinimumInteritemSpacingForItemAtIndexPath:(NSIndexPath *)indexPath{
    if ([self.collectionView.delegate respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)]) {
        id<UICollectionViewDelegateFlowLayout> delegate = (id<UICollectionViewDelegateFlowLayout>)self.collectionView.delegate;
        return [delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:indexPath.row];
    } else {
        return self.minimumInteritemSpacing;
    }
}

控制台出现的警告信息:


Logging only once for UICollectionViewFlowLayout cache mismatched frame.
UICollectionViewFlowLayout has cached frame mismatch for index path {length = 2, path = 0 - 1} - cached value: {{72, 30}, {52, 31}}; expected value: {{86, 30}, {52, 31}}
This is likely occurring because the flow layout subclass UICollectionViewLeftAlignFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them


这个警告在文件.m中重写父类的方法- (NSArray

    NSArray *attributesArrayReturn = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];

注:贴的文件代码是正确的。

PS:顺便看了一下数组的方法:

    NSMutableArray *originalArr = [NSMutableArray array];
    for (int i = 0; i < 3; i++) {
        UICollectionViewLayoutAttributes *attribute = [[UICollectionViewLayoutAttributes alloc]init];
        attribute.frame = CGRectMake(0, 0, 10, 20);
        [originalArr addObject:attribute];

    }
    NSArray *noCopyItems = [NSArray arrayWithArray:originalArr];
    NSArray *copyItems = [[NSArray alloc]initWithArray:originalArr copyItems:YES];
    if (noCopyItems[0] == originalArr[0]) {
        NSLog(@"noCopyItems[0] == originalArr[0]");
    }
//    NSLog(@"noCopyItems[0] = %p ,originalArr[0] = %p , copyItems[0] =%p",&(noCopyItems[0]),&(originalArr[0]),&(copyItems[0]));
    NSLog(@"noCopyItems[0] = %p ,originalArr[0] = %p , copyItems[0] =%p",noCopyItems[0],(originalArr[0]),(copyItems[0]));

    if (copyItems[0] == originalArr[0]) {
        NSLog(@"copyItems[0] == originalArr[0]");
    }else{
        NSLog(@"copyItems[0] != originalArr[0]");

    }

控制台输出:

2016-09-05 09:39:53.254 WebViewDemo[1101:343280] noCopyItems[0] == originalArr[0]
2016-09-05 09:39:53.254 WebViewDemo[1101:343280] noCopyItems[0] = 0x7ffcb8606800 ,originalArr[0] = 0x7ffcb8606800 , copyItems[0] =0x7ffcba80d560
2016-09-05 09:39:53.255 WebViewDemo[1101:343280] copyItems[0] != originalArr[0]
2016-09-05 09:39:57

数组的这个方法(-initWithArray:copyItems:):

- (instancetype)initWithArray:(NSArray<ObjectType> *)array 
                    copyItems:(BOOL)flag;

Initializes a newly allocated array using anArray as the source of data objects for the array.

大意:根据参数array里面的对象初始化一个新的数组。


参数2–flag:
If YES, each object in array receives a copyWithZone: message to create a copy of the object—objects must conform to the NSCopying protocol. In a managed memory environment, this is instead of the retain message the object would otherwise receive. The object copy is then added to the returned array.

If NO, then in a managed memory environment each object in array simply receives a retain message when it is added to the returned array.

大意:当flag = yes时,数组里面的对象都必须遵守NSCopying协议,并且每个对象会使用copyWithZone:方法创建该对象的一个备份。在内存管理环境中,这不是retain接收的对象。返回对象的副本,添加到数组中。
当flag = no,在管理内存环境中,每个对象在数组直接收到retain消息被添加到返回的数组。


那么又出现一个问题,关于数组的。

NSMutableArray *originalArr = [NSMutableArray array];
    for (int i = 0; i < 3; i++) {
        [originalArr addObject:[[NSString alloc] initWithFormat:@"index%d",i]];
    }
    NSArray *noCopyItems = [NSArray arrayWithArray:originalArr];
    NSArray *copyItems = [[NSArray alloc]initWithArray:originalArr copyItems:YES];
    if (noCopyItems[0] == originalArr[0]) {
        NSLog(@"noCopyItems[0] == originalArr[0]");
    }
    //打印元素地址
    NSLog(@"noCopyItems[0] = %p ,originalArr[0] = %p , copyItems[0] =%p",noCopyItems[0],(originalArr[0]),(copyItems[0]));

    if (copyItems[0] == originalArr[0]) {
        NSLog(@"copyItems[0] == originalArr[0]");
    }else{
        NSLog(@"copyItems[0] != originalArr[0]");
    }

控制台输出

2016-09-05 11:03:36.868 WebViewDemo[1388:767630] noCopyItems[0] == originalArr[0]
2016-09-05 11:03:36.868 WebViewDemo[1388:767630] noCopyItems[0] = 0xa00307865646e696 ,originalArr[0] = 0xa00307865646e696 , copyItems[0] =0xa00307865646e696
2016-09-05 11:03:36.868 WebViewDemo[1388:767630] copyItems[0] == originalArr[0]

这又是问什么,求解答?

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值