UICollectionViewLayoutAttributes
UICollectionViewLayoutAttributes是一個(gè)非常重要的類吕粹,屬性列表如下
@property (nonatomic) CGRect frame;
@property (nonatomic) CGPoint center;
@property (nonatomic) CGSize size;
@property (nonatomic) CATransform3D transform3D;
@property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGFloat alpha;
@property (nonatomic) NSInteger zIndex; // default is 0
@property (nonatomic, getter=isHidden) BOOL hidden;
UICollectionViewLayoutAttributes的實(shí)例中包含了諸如邊框蛉签、中心點(diǎn)壁袄、大小茅特、形狀丈咐、邊界袖裕、透明度燎猛、層次關(guān)系和是否隱藏等信息恋捆。當(dāng)UICollectionView在獲取布局時(shí)將針對(duì)每一個(gè)indexPath的部件(包括cell、SupplementaryView和DecorationView)重绷,向其上的UICollectionViewLayout實(shí)例詢問(wèn)該部件的布局信息沸停,這個(gè)布局信息,就是以UICollectionViewLayoutAttributes的實(shí)例方式給出的昭卓。
自定義UICollectionViewLayout
UICollectionViewLayout的功能是為UICollectionView提供布局信息愤钾,不僅包括cell的布局信息,也包括SupplementaryView和DecorationView的布局信息候醒。實(shí)現(xiàn)一個(gè)自定義的UICollectionViewLayout的常規(guī)做法是繼承UICollectionViewLayout绰垂,然后重載下列方法
// 返回collectionView的內(nèi)容尺寸
-(CGSize)collectionViewContentSize
/**
* 返回rect中的所有元素的布局屬性
* 返回的是包含UICollectionViewLayoutAttributes的數(shù)組
* UICollectionViewLayoutAttributes可以是cell、SupplementaryView或者DecorationView的信息通過(guò)不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的UICollectionViewLayoutAttributes
* + (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath
* + (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath
* + (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath
*/
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
// 返回對(duì)應(yīng)于indexPath的位置的cell的布局屬性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
// 返回對(duì)應(yīng)于indexPath位置的SupplementaryView的布局屬性火焰,如果沒(méi)有SupplementaryView可不重載
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
// 返回對(duì)應(yīng)于indexPath位置的DecorationView的布局屬性劲装,如果沒(méi)有DecorationView可不重載
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;
// 當(dāng)邊界發(fā)生改變時(shí),是否應(yīng)該刷新布局昌简。如果YES則在邊界變化時(shí)占业,將重新計(jì)算需要的布局信息。
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
初始化一個(gè)UICollectionViewLayout實(shí)例后纯赎,首先-(void)prepareLayout將被調(diào)用谦疾,之后-(CGSize)collectionViewContentSize被調(diào)用,以確定collectionView應(yīng)該占據(jù)的尺寸犬金。注意這里的尺寸不是指可是部分的尺寸念恍,應(yīng)該是所有內(nèi)容占據(jù)的尺寸六剥。接下來(lái)-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect被調(diào)用,獲取部件的布局信息峰伙。
CircleLayout---官方示例
參數(shù)聲明
@interface CircleLayout ()
@property (nonatomic, assign) CGPoint center;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, assign) NSInteger cellCount;
// arrays to keep track of insert, delete index paths
@property (nonatomic, strong) NSMutableArray *deleteIndexPaths;
@property (nonatomic, strong) NSMutableArray *insertIndexPaths;
@end
首先布局準(zhǔn)備中計(jì)算所需要用到的參數(shù)
-(void)prepareLayout
{
[super prepareLayout];
CGSize size = self.collectionView.frame.size;
_cellCount = [[self collectionView] numberOfItemsInSection:0];
_center = CGPointMake(size.width / 2.0, size.height / 2.0);
_radius = MIN(size.width, size.height) / 2.5;
}
然后按照UICollectionViewLayout子類的要求重載所需要的方法
// 返回collectionView的內(nèi)容尺寸
-(CGSize)collectionViewContentSize
{
return [self collectionView].frame.size;
}
// 返回對(duì)應(yīng)于indexPath的位置的cell的布局屬性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
{
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
attributes.center = CGPointMake(_center.x + _radius * cosf(2 * path.item * M_PI / _cellCount),
_center.y + _radius * sinf(2 * path.item * M_PI / _cellCount));
return attributes;
}
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray* attributes = [NSMutableArray array];
for (NSInteger i=0 ; i < self.cellCount; i++) {
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
[attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
}
return attributes;
}
增加刪除cell的動(dòng)畫
- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
// Keep track of insert and delete index paths
[super prepareForCollectionViewUpdates:updateItems];
self.deleteIndexPaths = [NSMutableArray array];
self.insertIndexPaths = [NSMutableArray array];
for (UICollectionViewUpdateItem *update in updateItems)
{
if (update.updateAction == UICollectionUpdateActionDelete)
{
[self.deleteIndexPaths addObject:update.indexPathBeforeUpdate];
}
else if (update.updateAction == UICollectionUpdateActionInsert)
{
[self.insertIndexPaths addObject:update.indexPathAfterUpdate];
}
}
}
- (void)finalizeCollectionViewUpdates
{
[super finalizeCollectionViewUpdates];
// release the insert and delete index paths
self.deleteIndexPaths = nil;
self.insertIndexPaths = nil;
}
// Note: name of method changed
// Also this gets called for all visible cells (not just the inserted ones) and
// even gets called when deleting cells!
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
// Must call super
UICollectionViewLayoutAttributes *attributes = [super initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath];
if ([self.insertIndexPaths containsObject:itemIndexPath])
{
// only change attributes on inserted cells
if (!attributes)
attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
// Configure attributes ...
attributes.alpha = 0.0;
attributes.center = CGPointMake(_center.x, _center.y);
}
return attributes;
}
// Note: name of method changed
// Also this gets called for all visible cells (not just the deleted ones) and
// even gets called when inserting cells!
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
// So far, calling super hasn't been strictly necessary here, but leaving it in
// for good measure
UICollectionViewLayoutAttributes *attributes = [super finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath];
if ([self.deleteIndexPaths containsObject:itemIndexPath])
{
// only change attributes on deleted cells
if (!attributes)
attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
// Configure attributes ...
attributes.alpha = 0.0;
attributes.center = CGPointMake(_center.x, _center.y);
attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);
}
return attributes;
}
在ViewController中為collectionView增加手勢(shì)識(shí)別
UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[self.collectionView addGestureRecognizer:tapRecognizer];
手勢(shì)響應(yīng)方法
#pragma mark - event response
- (void)handleTapGesture:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint initialPinchPoint = [sender locationInView:self.collectionView];
NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint];
if (tappedCellPath!=nil)
{
self.cellCount = self.cellCount - 1;
[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:@[tappedCellPath]];
} completion:nil];
}
else
{
self.cellCount = self.cellCount + 1;
[self.collectionView performBatchUpdates:^{
[self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
} completion:nil];
}
}
}
CircleLayout 中cell都分布在圓周上疗疟,點(diǎn)擊cell的話會(huì)將其從collectionView中移除,點(diǎn)擊空白處會(huì)加入一個(gè)cell瞳氓,加入和移除cell都會(huì)有動(dòng)畫效果.
效果圖如下