UICollectionView

引用自:http://www.jianshu.com/p/c59a5c92f859

一、UICollectionVIew基础

第一步:创建布局对象:

如果使用系统的布局类,我们通常是使用其UICollectionViewFlowLayout子类,通过这个子类来创建我们的布局对象。实例:UICollectionVIewFlowLayout *flowLayout = [UICollectionVIewFlowLayout new];

通过这个实例化的对象提供了一些基本的属性,用于帮助我们控制布局的方式,当然这个类也有其自身的协议,通过签署协议可以实现协议中的一些方法,这些方法可以帮助我们实现更加完善的布局。协议名字叫做:“UICollectionVIewDelegateFlowLayout”,这个协议也是实现基本CollectionVIew的三个协议中必须用到的,其他另外两个协议分别是:UICollectionViewDataSource(数据源)、UICollectionViewDelegate。

下面简单介绍一些关于这个类对象用于实现布局的属性有哪些:

@property(nonatomic)CGFloatminimumLineSpacing;// 这个用于指定每个Item的行间距,默认间距是10.

@property(nonatomic)CGFloatminimumInteritemSpacing;//用于指定每个单元的列间距。

@property(nonatomic)CGSizeitemSize;//指定每个cell的size.

@property(nonatomic)CGSizeestimatedItemSizeNS_AVAILABLE_IOS(8_0);//defaults to CGSizeZero - setting a non-zero size enables cells that self-size via -preferredLayoutAttributesFittingAttributes:

@property(nonatomic)UICollectionViewScrollDirectionscrollDirection;// default is UICollectionViewScrollDirectionVertical

@property(nonatomic)CGSizeheaderReferenceSize;

@property(nonatomic)CGSizefooterReferenceSize;//头部增添视图的size

@property(nonatomic)UIEdgeInsetssectionInset;//设置分区的内边距距离上左下右的距离,所以这个参数有四个参数。

// Set these properties to YES to get headers that pin to the top of the screen and footers that pin to the bottom while scrolling (similar to UITableView).

@property(nonatomic)BOOLsectionHeadersPinToVisibleBoundsNS_AVAILABLE_IOS(9_0);

@property(nonatomic)BOOLsectionFootersPinToVisibleBoundsNS_AVAILABLE_IOS(9_0);c//用于控制是否在当前窗口显示下一个分区的尾增添

第二步:通过布局对象创建UICollectionView,并设置相关属性,重点是给代理属性给定对象。

初始化UICollectionVIew的方法:

Frame:指定UICollectionView的Frame

collectionViewLayout:指定UICollectView的布局对象。

- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout*)layoutNS_DESIGNATED_INITIALIZER;

第三步:设置好各个重用标识

这里就不多说了,在代码的全局位置指定三个全局字符常量。

第四步:使用重用标识注册相关的重用池。

cell的重用池的注册方法:

Class:指定用哪个类作为cell,所以我们可以通过新建继承自UICollectionViewLayout的类来自定义cell.

- (void)registerClass:(nullableClass)cellClass forCellWithReuseIdentifier:(NSString*)identifier;

补充视图重用池的注册方法:

forSupplementaryViewOfKind:指定补充视图的类型

withReuseIdentifier:指定一个重用标识符

- (void)registerClass:(nullableClass)viewClass forSupplementaryViewOfKind:(NSString*)elementKind withReuseIdentifier:(NSString*)identifier;

第五步:实现UICollectionViewDataSource的几个代理方法(这里只提几个常用的)

#pragma mark ---- UICollectionViewDataSource

//  指定在一个CollectionView上分区的数量。

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

{

return 1;

}

// 指定在每个分区上Item的数量。

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

return _section0Array.count;

}

// 从重用池中取出UICollectionVIew的celle来给每个Item赋值。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];

cell.backgroundColor = [UIColor purpleColor];

return cell;

}

// 和UITableView类似,UICollectionView也可设置段头段尾

这个方法中的参数分别是:

collectionView:这个就不解释了,每个协议中必须传入的跟类。

viewForSupplementaryElementOfKind:根据传入的Kind值来判断到底添加的是那种类型的视图。

atIndexPath:这个参数通过传值来确定到底给那个分区补充视图。

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath

{

if([kind isEqualToString:UICollectionElementKindSectionHeader])

{

UICollectionReusableView *headerView = [_collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerId forIndexPath:indexPath];

if(headerView == nil)

{

headerView = [[UICollectionReusableView alloc] init];

}

headerView.backgroundColor = [UIColor grayColor];

return headerView;

}

else if([kind isEqualToString:UICollectionElementKindSectionFooter])

{

UICollectionReusableView *footerView = [_collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:footerId forIndexPath:indexPath];

if(footerView == nil)

{

footerView = [[UICollectionReusableView alloc] init];

}

footerView.backgroundColor = [UIColor lightGrayColor];

return footerView;

}

return nil;

}

// 暂时理解为:能够移动的Item,有待验证。

- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath

{

return YES;

}

//  暂时理解为:给能够移动的Item指定移动路径,有待验证。

moveItemAtIndexPath:能够一定的Item路径。

toIndexPath:移动到的目标路径。

- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath

{

}

第六步:实现UICollectionViewDelegateFlowLayout协议方法(这里的很多方法用属性的点语法同样可以实现,建议使用点语法。)

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

{

return (CGSize){cellWidth,cellWidth};

}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section

{

return UIEdgeInsetsMake(5, 5, 5, 5);

}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section

{

return 5.f;

}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section

{

return 5.f;

}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section

{

return (CGSize){ScreenWidth,44};

}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section

{

return (CGSize){ScreenWidth,22};

}

第七步:实现UICollectionViewDelegate方法

// 指定那些Item需要高亮显示

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath

{

return YES;

}

// 点击高亮

- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath

{

UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

cell.backgroundColor = [UIColor greenColor];

}

// 选中某item

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{

}

// 长按某item,弹出copy和paste的菜单

- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath

{

return YES;

}

// 使copy和paste有效

- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender

{

if ([NSStringFromSelector(action) isEqualToString:@"copy:"] || [NSStringFromSelector(action) isEqualToString:@"paste:"])

{

return YES;

}

return NO;

}

//

- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender

{

if([NSStringFromSelector(action) isEqualToString:@"copy:"])

{

//        NSLog(@"-------------执行拷贝-------------");

[_collectionView performBatchUpdates:^{

[_section0Array removeObjectAtIndex:indexPath.row];

[_collectionView deleteItemsAtIndexPaths:@[indexPath]];

} completion:nil];

}

else if([NSStringFromSelector(action) isEqualToString:@"paste:"])

{

NSLog(@"-------------执行粘贴-------------");

}

}

二、自定义布局

UICollectionView自定义布局

要自定义UICollectionView布局,就要子类化UICollectionViewLayout,然后重写它的一些方法以达到我们自定义布局的需求。下来我们来看看UICollectionViewLayout类里一些比较重要的方法:

- (void)prepareLayout;为layout显示做准备工作,你可以在该方法里设置一些属性。

- (CGSize)collectionViewContentSize;返回layout的size。

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;返回在collectionView的可见范围内(bounds)所有item对应的layoutAttrure对象装成的数组。collectionView的每个item都对应一个专门的UICollectionViewLayoutAttributes类型的对象来表示该item的一些属性,比如bounds,size,transform,alpha等。

- (UICollectionViewLayoutAttributes)layoutAttributesForItemAtIndexPath:(NSIndexPath)indexPath;传入indexPath,返回该indexPath对应的layoutAtture对象。

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;当当前layout的布局发生变动时,是否重写加载该layout。默认返回NO,若返回YES,则重新执行这俩方法:

- (void)prepareLayout;

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;返回layout“最终”的偏移量,何谓“最终”,手指离开屏幕时layout的偏移量不是最终的,因为它有惯性,当它停止时才是“最终”偏移量。

自定义插入删除动画

插入删除的操作

添加在哪触发:

UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithTitle:@"添加"

style:UIBarButtonItemStylePlain

target:self

action:@selector(addItemBtnClick:)];

self.navigationItem.rightBarButtonItem = btnItem;

添加的实现:

// 添加(插入item)

- (void)addItemBtnClick:(UIBarButtonItem *)btnItem

{

[_collectionView performBatchUpdates:^{

// 构造一个indexPath

NSIndexPath *indePath = [NSIndexPath indexPathForItem:_section0Array.count inSection:0];

[_collectionView insertItemsAtIndexPaths:@[indePath]]; // 然后在此indexPath处插入给collectionView插入一个item

[_section0Array addObject:@"x"]; // 保持collectionView的item和数据源一致

} completion:nil];

}

因为是练习Demo,所以暂时把删除的触发源写在了长按某Item弹出菜单的copy按钮里。实际中你可以自定义UICollectionViewCell,添加长按手势,长按抖动出现叉号,然后删除等,随你怎么做。

// copy and paste 的实现

- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender

{

if([NSStringFromSelector(action) isEqualToString:@"copy:"])

{

//        NSLog(@"-------------执行拷贝-------------");

[_collectionView performBatchUpdates:^{

[_section0Array removeObjectAtIndex:indexPath.row];

[_collectionView deleteItemsAtIndexPaths:@[indexPath]];

} completion:nil];

}

else if([NSStringFromSelector(action) isEqualToString:@"paste:"])

{

NSLog(@"-------------执行粘贴-------------");

}

}

自定义转场动画

推荐阅读更多精彩内容