iOS UICollectionView之自定義Layout

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)畫效果.
效果圖如下

CircleLayout.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末策彤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匣摘,更是在濱河造成了極大的恐慌店诗,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件音榜,死亡現(xiàn)場(chǎng)離奇詭異庞瘸,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赠叼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門恕洲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人梅割,你說(shuō)我怎么就攤上這事霜第。” “怎么了户辞?”我有些...
    開(kāi)封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵泌类,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我底燎,道長(zhǎng)刃榨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任双仍,我火速辦了婚禮枢希,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘朱沃。我一直安慰自己苞轿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布逗物。 她就那樣靜靜地躺著搬卒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪翎卓。 梳的紋絲不亂的頭發(fā)上契邀,一...
    開(kāi)封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音失暴,去河邊找鬼坯门。 笑死微饥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的古戴。 我是一名探鬼主播欠橘,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼允瞧!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蛮拔,我...
    開(kāi)封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤述暂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后建炫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體畦韭,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年肛跌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了艺配。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衍慎,死狀恐怖转唉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稳捆,我是刑警寧澤赠法,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站乔夯,受9級(jí)特大地震影響砖织,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜末荐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一侧纯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧甲脏,春花似錦眶熬、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至负乡,卻和暖如春牍白,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抖棘。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工茂腥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狸涌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓最岗,卻偏偏與公主長(zhǎng)得像帕胆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子般渡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容