界面实现如图所示的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]
这又是问什么,求解答?