iOS UICollectionView: The Complete Guide摘要
来源:互联网 发布:软件水平考试成绩查询 编辑:程序博客网 时间:2024/05/16 01:31
本文的内容来自iOS UICollectionView: The Complete Guide, Second Edition。文章对应的源码可以再http://ashfurrow.com/uicollectionview-the-complete-guide/处下载。
通用
设置cell的大小
如果使用了storyboard,可以在storyboard中直接设置
如果使用了UICollectionViewFlowLayout,可以在UICollectionViewFlowLayout中设置
surveyFlowLayout.itemSize = kMaxItemSize;
可以使用UICollectionViewDelegateFlowLayout中的- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
方法。
prepareForReuse
在自定义UICollectionViewCell时,可以重写-(void)prepareForReuse函数,用来在复用前进行一些clean up的操作。例如,UICollectionViewCell中有一个label,每次都要给label赋值,如下:
@interface AFCollectionViewCell : UICollectionViewCell@property (nonatomic, copy) NSString *text;@end#import "AFCollectionViewCell.h"@implementation AFCollectionViewCell{ UILabel *textLabel;}#pragma mark - Initialization- (id)initWithFrame:(CGRect)frame{ if (!(self = [super initWithFrame:frame])) return nil; self.backgroundColor = [UIColor whiteColor]; textLabel = [[UILabel alloc] initWithFrame:self.bounds]; textLabel.textAlignment = NSTextAlignmentCenter; textLabel.font = [UIFont boldSystemFontOfSize:20]; [self.contentView addSubview:textLabel]; return self;}#pragma mark - Overriden UICollectionViewCell methods-(void)prepareForReuse{ [super prepareForReuse]; self.text = @"";}#pragma mark - Overriden properties-(void)setText:(NSString *)text{ _text = [text copy]; textLabel.text = self.text;}@end
selected、highlighted
cell有两个重要的bool属性:selected和highlighted。highlighted完全由用户的交互决定,当用户的手指按下一个cell时,它就自动的变成highlighted。如果cell支持selection,当用户抬起他的手指,cell就变成selected。cell会保持selected,直到你写的一些代码使它unselected或者用户再次点击它。UICollectionViewCell的层级关系如下所示:
backgroundView如果被设置,就会永久的显示。当cell被selected,selectedBackgroundView就会被添加到view的层级中,当cell变成unselected,它就会被移除。
下面通过例子说明。通过如下代码来允许多选:
self.collectionView.allowsMultipleSelection = YES;
通过设置selectedBackgroundView来设置selected的背景view。重写setHighlighted来设置highlighted的样式。自定义的UICollectionViewCell如下:
@interface AFCollectionViewCell : UICollectionViewCell@property (nonatomic, strong) UIImage *image;@end@implementation AFCollectionViewCell{ UIImageView *imageView;}- (id)initWithFrame:(CGRect)frame{ if (!(self = [super initWithFrame:frame])) return nil; self.backgroundColor = [UIColor whiteColor]; imageView = [[UIImageView alloc] initWithFrame:CGRectInset(self.bounds, 10, 10)]; [self.contentView addSubview:imageView]; UIView *selectedBackgroundView = [[UIView alloc] initWithFrame:CGRectZero]; selectedBackgroundView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.8f]; self.selectedBackgroundView = selectedBackgroundView; return self;}#pragma mark - Overriden UICollectionViewCell methods-(void)prepareForReuse{ [super prepareForReuse]; self.backgroundColor = [UIColor whiteColor]; self.image = nil; //also resets imageView’s image}-(void)setHighlighted:(BOOL)highlighted{ [super setHighlighted:highlighted]; if (self.highlighted) { imageView.alpha = 0.8f; } else { imageView.alpha = 1.0f; }}#pragma mark - Overridden Properties-(void)setImage:(UIImage *)image{ _image = image; imageView.image = image;}
运行的效果如下:
自定义selection的相关方法
collectionView:shouldHighlightItemAtIndexPath:collectionView:shouldSelectItemAtIndexPath:collectionView:shouldDeselectItemAtIndexPath:
Supplementary views
data soruce为collection view提供了需要配置supplementary views所需的信息,但是supplementary views是由 UICollectionViewLayout对象布局的。
UICollectionViewFlowLayout类中有两个supplementary views:header和footer。
自定义一个AFCollectionHeaderView,如下:
@interface AFCollectionHeaderView : UICollectionReusableView@property (nonatomic, copy) NSString *text;@end#import "AFCollectionHeaderView.h"@implementation AFCollectionHeaderView{ UILabel *textLabel;}- (id)initWithFrame:(CGRect)frame{ if (!(self = [super initWithFrame:frame])) return nil; textLabel = [[UILabel alloc] initWithFrame:CGRectInset(CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame)), 30, 10)]; textLabel.backgroundColor = [UIColor clearColor]; textLabel.textColor = [UIColor whiteColor]; textLabel.font = [UIFont boldSystemFontOfSize:20]; [self addSubview:textLabel]; return self;}-(void)prepareForReuse{ [super prepareForReuse]; [self setText:@""];}-(void)setText:(NSString *)text{ _text = [text copy]; [textLabel setText:text];}@end
在使用时,设置headerReferenceSize
surveyFlowLayout.headerReferenceSize = CGSizeMake(60, 50);
headerReferenceSize是用来告知collection view的布局对象,header是多大。如果你忘记设置,默认就是0,你的header就不会显示。
当水平滚动时,只有你指定的width被使用,header被垂直的拉伸来填满它的空间。当垂直滚动时,只有height被使用,header被水平拉伸来填满它的空间。如下图所示:
注册supplementary views
[surveyCollectionView registerClass:[AFCollectionHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:HeaderIdentifier];
使用header,实现UICollectionViewDataSource的- (UICollectionReusableView )collectionView:(UICollectionView )collectionView viewForSupplementaryElementOfKind:(NSString )kind atIndexPath:(NSIndexPath )indexPath方法。
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{ //Provides a view for the headers in the collection view AFCollectionHeaderView *headerView = (AFCollectionHeaderView *)[collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:HeaderIdentifier forIndexPath:indexPath]; if (indexPath.section == 0) { //If this is the first header, display a prompt to the user [headerView setText:@"Tap on a photo to start the recommendation engine."]; } else if (indexPath.section <= currentModelArrayIndex) { //Otherwise, display a prompt using the selected photo from the previous section AFSelectionModel *selectionModel = selectionModelArray[indexPath.section - 1]; AFPhotoModel *selectedPhotoModel = [self photoModelForIndexPath:[NSIndexPath indexPathForItem:selectionModel.selectedPhotoModelIndex inSection:indexPath.section - 1]]; [headerView setText:[NSString stringWithFormat:@"Because you liked %@...", selectedPhotoModel.name]]; } return headerView;}
更新数据
performBatchUpdates:completion:是一个用来更新collection view的block。修改collection view内容的方法如下:
insertSections:deleteSections:reloadSections:moveSection:toSection:insertItemsAtIndexPaths:deleteItemsAtIndexPaths:reloadItemsAtIndexPaths:moveItemAtIndexPath:toIndexPath:
支持Cut/Copy/Paste
首先让collection view支持menus。
-(BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath{ return YES;}
接下来,collection view会询问其delegate,它能执行不同的action。
-(BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender{ if ([NSStringFromSelector(action) isEqualToString:@"copy:"]) { return YES; } return NO;}
接下来就是执行对应的操作:
-(void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender{ if ([NSStringFromSelector(action) isEqualToString:@"copy:"]) { UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; [pasteboard setString:[[self photoModelForIndexPath:indexPath] name]]; }}
UICollectionViewFlowLayout
UICollectionViewLayout是一个抽象类,是用来被继承的。每一个collection view都有一个布局对象与其紧密相连,这个布局对象用来布局内容。Layout并不关心布局的数据。
UICollectionViewFlowLayout其实是一个grid布局。collection view的调用过程如下:
- collection view询问data source有个显示内容的信息。包括section的多少,item的多少,以及每个section的supplymentary view。
- collection view从其布局对象获取有关显示cell,supplementary view和decoration view的信息。这些信息都存储在一个叫做UICollectionViewLayoutAttributes的类中。
- 最后,collection view会转发这些布局信息给cell supplementary view和decoration view
当现在的layout无效时,可以使用invalidateLayout
来重新布局。
继承UICollectionViewFlowLayout
为什么选择继承?原因如下:
- 除了使用代理方法外,修改继承布局对象的属性。To modify the attributes of the layout you’re subclassing beyond what is possible with delegate methods
- 在布局中嵌入decoration view。To incorporate decoration views in your layout
- 添加新的类型的supplementary view。To add new kinds of supplementary views
- 扩展UICollectionViewLayoutAttributes,为你布局类添加新的属性。ToextendUICollectionViewLayoutAttributestoaddnewattributesofitems for your layout class to manage
- 添加手势支持。To add gesture support
- 自定义插入、更新、删除的动画。To customize the animation of insertion, update, and deletion updates to the collection view
例如如下图所示,由于cell并不是有同样的size,cell就不会垂直对齐。UICollectionViewFlowLayout并没有提供“均匀”分布。因此,我们可以继承UICollectionViewFlowLayout来完成。
创建AFCollectionViewFlowLayout继承自UICollectionViewFlowLayout。接着我们把大部分的布局逻辑从view controller中移出来。
#import <UIKit/UIKit.h>#define kMaxItemDimension 200.0f#define kMaxItemSize CGSizeMake(kMaxItemDimension, kMaxItemDimension)extern NSString * const AFCollectionViewFlowLayoutBackgroundDecoration;@interface AFCollectionViewFlowLayout : UICollectionViewFlowLayout@end
然后实现init方法,设置一些属性
-(id)init{ if (!(self = [super init])) return nil; self.sectionInset = UIEdgeInsetsMake(30.0f, 80.0f, 30.0f, 20.0f); self.minimumInteritemSpacing = 20.0f; self.minimumLineSpacing = 20.0f; self.itemSize = kMaxItemSize; self.headerReferenceSize = CGSizeMake(60, 70); [self registerClass:[AFDecorationView class] forDecorationViewOfKind:AFCollectionViewFlowLayoutBackgroundDecoration]; insertedSectionSet = [NSMutableSet set]; return self;}
接下来需要重写UICollectionViewFlowLayout
的两个方法:layoutAttributesForElementsInRect:
和layoutAttributesForItemAtIndexPath:
,这两个方法在collection view布局它的cell,supplementary view和decoration view的时候,会被调用。在此例中,我们会创建一个私有的、第三方德方法,叫做applyLayoutAttributes
,我们稍后讨论。
-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)attributes{ // Check for representedElementKind being nil, indicating this is a cell and not a header or decoration view if (attributes.representedElementKind == nil) { CGFloat width = [self collectionViewContentSize].width; CGFloat leftMargin = [self sectionInset].left; CGFloat rightMargin = [self sectionInset].right; NSUInteger itemsInSection = [[self collectionView] numberOfItemsInSection:attributes.indexPath.section]; CGFloat firstXPosition = (width - (leftMargin + rightMargin)) / (2 * itemsInSection); CGFloat xPosition = firstXPosition + (2*firstXPosition*attributes.indexPath.item); attributes.center = CGPointMake(leftMargin + xPosition, attributes.center.y); attributes.frame = CGRectIntegral(attributes.frame); }}#pragma mark - Overridden Methods#pragma mark Cell Layout-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSArray *attributesArray = [super layoutAttributesForElementsInRect:rect]; NSMutableArray *newAttributesArray = [NSMutableArray array]; for (UICollectionViewLayoutAttributes *attributes in attributesArray) { [self applyLayoutAttributes:attributes]; if (attributes.representedElementCategory == UICollectionElementCategorySupplementaryView) { UICollectionViewLayoutAttributes *newAttributes = [self layoutAttributesForDecorationViewOfKind:AFCollectionViewFlowLayoutBackgroundDecoration atIndexPath:attributes.indexPath]; [newAttributesArray addObject:newAttributes]; } } attributesArray = [attributesArray arrayByAddingObjectsFromArray:newAttributesArray]; return attributesArray;}-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath]; [self applyLayoutAttributes:attributes]; return attributes;}
重写的两个方法都调用了其superclass的实现,用来获取所有的UICollectionViewFlowLayout行为。在我们获取默认的属性后,我们可以做出修改。
在applyLayoutAttributes:方法中,我们首先检查representedElementKind属性
。对于正常的UICollectionViewCells
,会是nil。否则的话,它将会是collection view注册的supplementary view类型。在本例中,将会是UICollectionElementKindSectionHeader
。
UICollectionView的Decoration View
给UICollectionView加上Decoration View。请参考How to Add a Decoration View to a UICollectionView
与cell和supplementary view不同的是,decoration view不是data-driven,它是layout-driven。如何在collection view中添加decoration view呢?
- 创建一个decoration view,继承自UICollectionReusableView
- 创建UICollectionViewLayout的子类。也可以继承自UICollectionViewFlowLayout。
- 在你的layout中布局注册decoration view。
- 在layoutAttributesForElementsInRect:方法中,为你的decoration view 返回适当的属性。
- 实现layoutAttributesForDecorationViewOfKind:atIndexPath:方法,为指定的decoration view返回attributes。
注册decoration view
[self registerClass:[AFDecorationView class] forDecorationViewOfKind:AFCollectionViewFlowLayoutBackgroundDecoration];
实现-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath
方法:
-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath]; if ([decorationViewKind isEqualToString:AFCollectionViewFlowLayoutBackgroundDecoration]) { UICollectionViewLayoutAttributes *tallestCellAttributes; NSInteger numberOfCellsInSection = [self.collectionView numberOfItemsInSection:indexPath.section]; for (NSInteger i = 0; i < numberOfCellsInSection; i++) { NSIndexPath *cellIndexPath = [NSIndexPath indexPathForItem:i inSection:indexPath.section]; UICollectionViewLayoutAttributes *cellAttribtes = [self layoutAttributesForItemAtIndexPath:cellIndexPath]; if (CGRectGetHeight(cellAttribtes.frame) > CGRectGetHeight(tallestCellAttributes.frame)) { tallestCellAttributes = cellAttribtes; } } CGFloat decorationViewHeight = CGRectGetHeight(tallestCellAttributes.frame) + self.headerReferenceSize.height; layoutAttributes.size = CGSizeMake([self collectionViewContentSize].width, decorationViewHeight); layoutAttributes.center = CGPointMake([self collectionViewContentSize].width / 2.0f, tallestCellAttributes.center.y); layoutAttributes.frame = CGRectIntegral(layoutAttributes.frame); // Place the decoration view behind all the cells layoutAttributes.zIndex = -1; } return layoutAttributes;}
添加动画
UICollectionViewLayout本身是支持动画的。主要要使用的方法有:
- initialLayoutAttributesForAppearingItemAtIndexPath: 在collection view的一个item被加入或者更新时都会调用。可以使用它,为动画开始时,给item提供一些初始的属性。
- finalLayoutAttributesForDisappearingItemAtIndexPath:
- prepareForCollectionViewUpdates:
- finalizeCollectionViewUpdates
例如
-(void)prepareForCollectionViewUpdates:(NSArray *)updateItems{ [super prepareForCollectionViewUpdates:updateItems]; [updateItems enumerateObjectsUsingBlock:^(UICollectionViewUpdateItem *updateItem, NSUInteger idx, BOOL *stop) { if (updateItem.updateAction == UICollectionUpdateActionInsert) { [insertedSectionSet addObject:@(updateItem.indexPathAfterUpdate.section)]; } }];}-(void)finalizeCollectionViewUpdates{ [super finalizeCollectionViewUpdates]; [insertedSectionSet removeAllObjects];}-(UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingDecorationElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)decorationIndexPath{ //returning nil will cause a crossfade UICollectionViewLayoutAttributes *layoutAttributes; if ([elementKind isEqualToString:AFCollectionViewFlowLayoutBackgroundDecoration]) { if ([insertedSectionSet containsObject:@(decorationIndexPath.section)]) { layoutAttributes = [self layoutAttributesForDecorationViewOfKind:elementKind atIndexPath:decorationIndexPath]; layoutAttributes.alpha = 0.0f; layoutAttributes.transform3D = CATransform3DMakeTranslation(-CGRectGetWidth(layoutAttributes.frame), 0, 0); } } return layoutAttributes;}-(UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath{ //returning nil will cause a crossfade UICollectionViewLayoutAttributes *layoutAttributes; if ([insertedSectionSet containsObject:@(itemIndexPath.section)]) { layoutAttributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath]; layoutAttributes.transform3D = CATransform3DMakeTranslation([self collectionViewContentSize].width, 0, 0); } return layoutAttributes;}
- iOS UICollectionView: The Complete Guide摘要
- 读书摘要:C++ Template:The Complete Guide
- ECL - The Complete Guide
- iOS Application Programming Guide 摘要
- iOS Application Programming Guide 摘要
- iOS Application Programming Guide 摘要
- 读书摘要—C++:The Complete Reference
- The Complete Cisco VPN Configuration Guide
- Voice Over WLANS: The Complete Guide
- Expert CAD Management: The Complete Guide
- The Complete Guide to C++ Strings
- The Complete Guide to Windows Server 2008
- The Complete Guide to C++ Strings
- View Programming Guide for iOS 摘要
- View Controller Programming Guide for iOS摘要
- View Programming Guide for iOS 摘要
- Slimming Down Windows XP: The Complete Guide 【 10章完整版 】
- 《The Complete Effect and HLSL Guide》翻译连载(一)
- hihocoder 1228 Mission Impossible 6(2015北京网赛 B)
- 子序列的最大和
- Python+OpenCV学习(19)---摄像机位姿估计
- 推荐丨产品经理最常用的13款工具
- 杭电acm1008
- iOS UICollectionView: The Complete Guide摘要
- 软件性能测试的本质
- android项目epub格式电子书开源开发
- 计算器
- 战略,定一个大的战略
- 信息熵的直观理解
- 0919Android基础动画
- 做好软件测试需要具备的思维方式
- 同步与异步、阻塞与非阻塞