UICollectionView自定义布局(石工布局)
效果:
MKMasonryViewLayout调用顺序
- numberOfSectionsInCollectionView(dataSource)
- numberOfItemsInSection(dataSource)
- prepareLayout (layout)
- (初始化布局)(调用了heightForItemAtIndexPath)
- heightForItemAtIndexPath(delegate)
- collectionViewContentSize(layout)
- layoutAttributesForElementsInRect(layout)(计算给定区域中cell的位置 ) + collectionViewContentSize
- (每次调用layoutAttributesForElementsInRect之后还会调用一次这个方法,所以可以计算一次然后保存这个值ContentSize)
- cellForItemAtIndexPath (datasource)
MKMasonryViewLayout.h
#import <UIKit/UIKit.h>
@class MKMasonryViewLayout;
@protocol MKMasonryViewLayoutDelegate <NSObject>
@required
- (CGFloat) collectionView:(UICollectionView*) collectionView
layout:(MKMasonryViewLayout*) layout
heightForItemAtIndexPath:(NSIndexPath*) indexPath;
@end
@interface MKMasonryViewLayout : UICollectionViewLayout
@property (nonatomic, assign) NSUInteger numberOfColumns;
@property (nonatomic, assign) CGFloat interItemSpacing;
@property (weak, nonatomic) IBOutlet id<MKMasonryViewLayoutDelegate> delegate;
@end
MKMasonryViewLayout.m
#import "MKMasonryViewLayout.h"
@interface MKMasonryViewLayout (/*Private Methods*/)
@property (nonatomic, strong) NSMutableDictionary *lastYValueForColumn;
@property (strong, nonatomic) NSMutableDictionary *layoutInfo;
@end
@implementation MKMasonryViewLayout
-(void) prepareLayout {
NSLog(@" prepareLayout ");
self.numberOfColumns = 3;//一行cell的数量
self.interItemSpacing = 12.5;//每行之间cell的间距
self.lastYValueForColumn = [NSMutableDictionary dictionary];
CGFloat currentColumn = 0;
CGFloat fullWidth = self.collectionView.frame.size.width;
CGFloat availableSpaceExcludingPadding = fullWidth - (self.interItemSpacing * (self.numberOfColumns + 1));
//计算cell宽度
CGFloat itemWidth = availableSpaceExcludingPadding / self.numberOfColumns;
//储存 UICollectionViewLayoutAttributes
self.layoutInfo = [NSMutableDictionary dictionary];
NSIndexPath *indexPath;
NSInteger numSections = [self.collectionView numberOfSections];
for(NSInteger section = 0; section < numSections; section++) {
NSInteger numItems = [self.collectionView numberOfItemsInSection:section];
for(NSInteger item = 0; item < numItems; item++){
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//计算物件的位置
CGFloat x = self.interItemSpacing + (self.interItemSpacing + itemWidth) * currentColumn;
CGFloat y = [self.lastYValueForColumn[@(currentColumn)] doubleValue];
CGFloat height = [((id<MKMasonryViewLayoutDelegate>)self.collectionView.delegate)
collectionView:self.collectionView
layout:self
heightForItemAtIndexPath:indexPath];
itemAttributes.frame = CGRectMake(x, y, itemWidth, height);
y+= height;
y += self.interItemSpacing;
self.lastYValueForColumn[@(currentColumn)] = @(y);
currentColumn ++;
if(currentColumn == self.numberOfColumns) currentColumn = 0;
//存入layoutInfo
self.layoutInfo[indexPath] = itemAttributes;
}
}
}
//计算给定区域中的物件 rect其实就是当前可视区域的rect
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSLog(@" layoutAttributesForElementsInRect rect is %f %f %f %f",rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
UICollectionViewLayoutAttributes *attributes,
BOOL *stop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[allAttributes addObject:attributes];
}
}];
return allAttributes;
}
//计算集合视图的大小 每次调用layoutAttributesForElementsInRect之后都会调用此方法,所以可以计算一次然后保存这个值ContentSize
-(CGSize) collectionViewContentSize {
NSLog(@" collectionViewContentSize " );
NSUInteger currentColumn = 0;
CGFloat maxHeight = 0;
do {
CGFloat height = [self.lastYValueForColumn[@(currentColumn)] doubleValue];
if(height > maxHeight)
maxHeight = height;
currentColumn ++;
} while (currentColumn < self.numberOfColumns);
NSLog(@" collectionViewContentSize %f %f ",self.collectionView.frame.size.width,maxHeight);
return CGSizeMake(self.collectionView.frame.size.width, maxHeight);
}
@end