寫(xiě)這篇文章的目的 一個(gè)原因是想把在開(kāi)發(fā)中遇到的一些常見(jiàn)的問(wèn)題通過(guò)文章的形式記錄和保留 以后可以沒(méi)事上來(lái)看看 一個(gè)原因是因?yàn)閷?duì)TableView不能任意隨我定制“深?lèi)和唇^” 特意對(duì)CollectionView進(jìn)行改造讓他能被我為所欲為 甚至替代UITableView
代碼地址 https://github.com/evernoteHW/UICollectionViewLayoutDemo/tree/master/UICollectionViewLayoutDemo
簡(jiǎn)單描述下我的思路 方便剁手黨 特意貼出代碼
1 利用Runtime機(jī)制擴(kuò)展屬性
創(chuàng)建UICollectionView 類(lèi)目 Category 添加兩個(gè)屬性headerView和footerView
- (UIView *)hw_collectionHeaderView
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setHw_collectionHeaderView:(UIView *)hw_collectionHeaderView
{
[self.hw_collectionHeaderView removeFromSuperview];
objc_setAssociatedObject(self, @selector(hw_collectionHeaderView), hw_collectionHeaderView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:hw_collectionHeaderView];
[self.collectionViewLayout invalidateLayout];
}
- (UIView *)hw_collectionFooterView
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setHw_collectionFooterView:(UIView *)hw_collectionFooterView
{
[self.hw_collectionFooterView removeFromSuperview];
objc_setAssociatedObject(self, @selector(hw_collectionFooterView), hw_collectionFooterView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self addSubview:hw_collectionFooterView];
[self.collectionViewLayout invalidateLayout];
}
2 子類(lèi)化 SubClass UICollectionViewLayout
第一步 改寫(xiě)prepareLayout </br>
第一小步 設(shè)定Item大小
for (NSInteger item = 0; item < itemCount; item++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
UICollectionViewLayoutAttributes *itemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGFloat item_x = self.itemInsets.left;
CGFloat item_y = top + itemHeight + self.itemInsets.top;
CGFloat item_width = self.itemSize.width - self.itemInsets.left - self.itemInsets.right;
CGFloat item_height = self.itemSize.height - self.itemInsets.top - self.itemInsets.bottom;
itemAttributes.frame = CGRectMake(item_x,item_y, item_width, item_height);
cellLayoutInfo[indexPath] = itemAttributes;
itemHeight += (self.itemSize.height);
}
第二小步 設(shè)定Header大小
NSIndexPath *headerIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
UICollectionViewLayoutAttributes *headerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:HWCollectionElementKindSectionHeader withIndexPath:headerIndexPath];
CGFloat headerHeight = self.headerHeight;
if ([self.layoutDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
headerHeight = [self.layoutDelegate collectionView:self.collectionView layout:self referenceSizeForHeaderInSection:section];
}
headerAttributes.frame = CGRectMake(0, top, self.itemSize.width, headerHeight);
headerLayoutInfo[headerIndexPath] = headerAttributes;
top += headerHeight;
第三小步 設(shè)定Footer大小
NSIndexPath *footerIndexPath = [NSIndexPath indexPathForItem:itemCount - 1 inSection:section];
UICollectionViewLayoutAttributes *footerAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:HWCollectionElementKindSectionFooter withIndexPath:footerIndexPath];
CGFloat footerHeight = self.footerHeight;
if ([self.layoutDelegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]) {
footerHeight = [self.layoutDelegate collectionView:self.collectionView layout:self referenceSizeForFooterInSection:section];
}
footerAttributes.frame = CGRectMake(0, top, self.itemSize.width, footerHeight);
footerLayoutInfo[footerIndexPath] = footerAttributes;
top += footerHeight;
第四小步 設(shè)定 裝飾視圖(用處:設(shè)定組與組的背景) 這個(gè)比較特殊或者分割線(xiàn) 比如類(lèi)似這樣的 組的背景可能是個(gè)圖片可能組頭視圖和組所有的cell公用一個(gè)背景圖 這個(gè)比較特殊
NSIndexPath *decorationIndexPath = [NSIndexPath indexPathForItem:0 inSection:section];
UICollectionViewLayoutAttributes *decorationAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:HWCollectionElementKindSectionDecoration withIndexPath:decorationIndexPath];
CGFloat decorationHeight = CGRectGetMaxY(footerAttributes.frame) - CGRectGetMinY(headerAttributes.frame);
decorationAttributes.frame = CGRectMake(0, CGRectGetMinY(headerAttributes.frame), self.itemSize.width, decorationHeight);
decorationAttributes.zIndex = -1;
decorationLayoutInfo[decorationIndexPath] = decorationAttributes;
這里需要說(shuō)明的是 zIndex = -1 總是處于最底端
第五小步 確定collectionHeaderView和collectionFooterView 位置
和普通的組頭部不同 這個(gè)可以類(lèi)似對(duì)比UITableView的tableHeaderView和tableFooterView 也很簡(jiǎn)單 如下
collectionHeaderView
CGFloat top = 0.0;
if (self.collectionView.hw_collectionHeaderView) {
top += self.collectionView.hw_collectionHeaderView.bounds.size.height;
}
CollectionFooterView
if (self.collectionView.hw_collectionFooterView) {
self.collectionView.hw_collectionFooterView.frame = CGRectMake(self.collectionView.hw_collectionFooterView.frame.origin.x, top , self.collectionView.hw_collectionFooterView.frame.size.width, self.collectionView.hw_collectionFooterView.frame.size.height)
;
top += self.collectionView.hw_collectionFooterView.bounds.size.height;
}
第六小步 將設(shè)定的大小全部存儲(chǔ)起來(lái)
我和別人的比較特殊很多使用二位數(shù)組的方式 我是用字典 字典這樣的存儲(chǔ)方式的好處是我可以任意添加一個(gè)Kind類(lèi)型 然后設(shè)置對(duì)于的Attribute大小 在任何地方加入都可以 最好存起來(lái) 還有一個(gè)好處是會(huì)在下面的 layoutAttributesForElementsInRect中體現(xiàn)出來(lái)
newLayoutInfo[HWLayoutItemCellKind] = cellLayoutInfo;
newLayoutInfo[HWLayoutHeaderKind] = headerLayoutInfo;
newLayoutInfo[HWLayoutFooterKind] = footerLayoutInfo;
newLayoutInfo[HWLayoutDecorationKind] = decorationLayoutInfo;
self.layoutInfo = newLayoutInfo;
第二步 確定在Rect范圍內(nèi)返回的Attribute數(shù)組 </br>
第二個(gè)好處是 這個(gè)方法是通用的不管你PrePareLayout方法怎么干 這些都一成不變的 這個(gè)方法的含義是在Rect范圍內(nèi) 意思是說(shuō) 只是返回Recf范圍內(nèi)的Attribute數(shù)組 不是所有的 這個(gè)注意一下
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
[self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSString *elementIdentifier,
NSDictionary *elementsInfo,
BOOL *stop) {
[elementsInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
UICollectionViewLayoutAttributes *attributes,
BOOL *innerStop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[allAttributes addObject:attributes];
}
}];
}];
return allAttributes;
}
第三步 確定 collectionViewContentSize </br>
之前設(shè)定的 第三個(gè)好處是 我再取得 collectionViewContentSize 取得最后一個(gè)Attribute 拿到 CGRectGetMaxY 如果有我之前通過(guò)類(lèi)目方法擴(kuò)展的collectionHeaderView 做一個(gè)判斷即可
- (CGSize)collectionViewContentSize
{
NSInteger sectionCount = [self.collectionView numberOfSections];
if (sectionCount <= 0) return [super collectionViewContentSize];
NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:sectionCount - 1];
UICollectionViewLayoutAttributes *footAttributes = nil;
NSDictionary *dic = self.layoutInfo[HWLayoutFooterKind];
if (dic.count > 0) {
footAttributes = self.layoutInfo[HWLayoutFooterKind][[NSIndexPath indexPathForRow:numberOfItemsInSection - 1 inSection:sectionCount - 1]];
}else{
footAttributes = self.layoutInfo[HWLayoutItemCellKind][[NSIndexPath indexPathForRow:numberOfItemsInSection - 1 inSection:sectionCount - 1]];
}
CGFloat height = CGRectGetMaxY(footAttributes.frame);
if (self.collectionView.hw_collectionFooterView) {
height += CGRectGetHeight(self.collectionView.hw_collectionFooterView.frame);
}
return CGSizeMake(self.collectionView.bounds.size.width, height);
}
第四步 設(shè)定Layout代理 改裝外部可以控制 </br>
設(shè)定代理 代理方法如下 當(dāng)如你如果是比如固定大小的可以通過(guò)設(shè)定諸如
@property (nonatomic) UIEdgeInsets itemInsets;
@property (nonatomic) CGSize itemSize;
@property (nonatomic) CGFloat interItemSpacingY;
@property (nonatomic) NSInteger numberOfColumns;
@property (nonatomic, assign) CGFloat headerHeight;
@property (nonatomic, assign) CGFloat footerHeight;
也可以通過(guò)設(shè)定代理隨意定制
@protocol HWCollectionViewWaterfallLayoutDelegate <NSObject>
@optional
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
//組與組的最小距離
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
//組頭高度
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
//組尾高度
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(HWCollectionViewWaterfallLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;
@end
End ----
項(xiàng)目效果圖

至此所有的工作都做完了
注意: 我這個(gè)只是針對(duì)TableView的仿造還沒(méi)有對(duì)流式布局進(jìn)行進(jìn)一步處理 下一篇文章說(shuō)明 如何定制 比如蘋(píng)果自帶瀏覽器圖片等功能 有什么問(wèn)題可以私信我或者在在下面評(píng)論 如果還不錯(cuò)給個(gè)Star吧