UICollectionView(一)——整體總結(jié)

前言

這幾天有時間看了下UICollectionView的東西,才發(fā)覺它真的非常強(qiáng)大霉晕,很有必要好好學(xué)習(xí)學(xué)習(xí)牺堰。以前雖然用過幾次颅围,但沒有系統(tǒng)的整理總結(jié)過院促。這兩天我為UICollectionView做一個比較全面的整理常拓。包括基本使用自定義布局茎辐,自定義插入刪除動畫拖陆,自定義轉(zhuǎn)場動畫等幾部分。好了懊亡,開始依啰。

UICollectionView相對于UITableView可以說是青出于藍(lán)而勝于藍(lán),它和UITableView很相似店枣,但它要更加強(qiáng)大孔飒。
UITableView的布局形式比較單一灌闺,局限于行列表,而UICollectionView的強(qiáng)大之處在于把視圖布局分離出來成為一個獨立的類坏瞄,你想實現(xiàn)怎樣的視圖布局桂对,就子類化這個類并在其中實現(xiàn)鸠匀。

UICollectionView基礎(chǔ)

  • UICollectionViewFlowLayout:視圖布局對象(流視圖:一行排滿蕉斜,自動排到下行),繼承自UICollectionViewLayout缀棍。
    UICollectionViewLayout有個collectionView屬性宅此,
    所有的視圖布局對象都繼承自UICollectionViewLayout。若我們要自定義布局對象爬范,我們一般繼承UICollectionViewFlowLayout就可以了父腕。
  • 需要實現(xiàn)三個協(xié)議;UICollectionViewDataSource(數(shù)據(jù)源)青瀑、UICollectionViewDelegateFlowLayout(視圖布局)璧亮、UICollectionViewDelegate。
    可以看得出斥难,除了視圖布局枝嘶,UICollectionView幾乎和UITableView一樣,但這也正是它的強(qiáng)大之處哑诊。
1.創(chuàng)建UICollectionView視圖
- (void)loadCollectionView
{
    _customLayout = [[CustomCollectionViewLayout alloc] init]; // 自定義的布局對象
    _collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:_customLayout];
    _collectionView.backgroundColor = [UIColor whiteColor];
    _collectionView.dataSource = self;
    _collectionView.delegate = self;
    [self.view addSubview:_collectionView];
    
    // 注冊cell群扶、sectionHeader、sectionFooter
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:cellId];
    [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerId];
    [_collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:footerId];
}

需要注意的是這幾行代碼的位置镀裤,及const的位置竞阐。(我經(jīng)常搞亂)

@implementation YWViewController

// 注意const的位置
static NSString *const cellId = @"cellId";
static NSString *const headerId = @"headerId";
static NSString *const footerId = @"footerId";


- (void)viewDidLoad
{
2.實現(xiàn)UICollectionViewDataSource的幾個代理方法

#pragma mark ---- UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _section0Array.count;
}


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:cellId forIndexPath:indexPath];
    cell.backgroundColor = [UIColor purpleColor];
    
    return cell;
}

// 和UITableView類似,UICollectionView也可設(shè)置段頭段尾
- (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;
}

- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}


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




#pragma mark ---- 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};
}




#pragma mark ---- UICollectionViewDelegate

- (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(@"-------------執(zhí)行拷貝-------------");
        [_collectionView performBatchUpdates:^{
            [_section0Array removeObjectAtIndex:indexPath.row];
            [_collectionView deleteItemsAtIndexPaths:@[indexPath]];
        } completion:nil];
    }
    else if([NSStringFromSelector(action) isEqualToString:@"paste:"])
    {
        NSLog(@"-------------執(zhí)行粘貼-------------");
    }
}

UICollectionView自定義布局

要自定義UICollectionView布局馁菜,就要子類化UICollectionViewLayout,然后重寫它的一些方法以達(dá)到我們自定義布局的需求铃岔。下來我們來看看UICollectionViewLayout類里一些比較重要的方法:

  • - (void)prepareLayout;為layout顯示做準(zhǔn)備工作,你可以在該方法里設(shè)置一些屬性峭火。
  • - (CGSize)collectionViewContentSize;返回layout的size毁习。
  • *- (NSArray )layoutAttributesForElementsInRect:(CGRect)rect;返回在collectionView的可見范圍內(nèi)(bounds)所有item對應(yīng)的layoutAttrure對象裝成的數(shù)組。collectionView的每個item都對應(yīng)一個專門的UICollectionViewLayoutAttributes類型的對象來表示該item的一些屬性卖丸,比如bounds,size,transform,alpha等纺且。
  • **- (UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath;傳入indexPath,返回該indexPath對應(yīng)的layoutAtture對象稍浆。
  • **- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds; **當(dāng)當(dāng)前l(fā)ayout的布局發(fā)生變動時载碌,是否重寫加載該layout猜嘱。默認(rèn)返回NO,若返回YES嫁艇,則重新執(zhí)行這倆方法:
  • - (void)prepareLayout;
  • - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
  • - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity;返回layout“最終”的偏移量朗伶,何謂“最終”,手指離開屏幕時layout的偏移量不是最終的步咪,因為它有慣性论皆,當(dāng)它停止時才是“最終”偏移量。

下面這兩個方法一般用于自定義插入刪除時的動畫猾漫,后面再說点晴。

  • **- (UICollectionViewLayoutAttributes )initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath )itemIndexPath;

  • **- (nullable UICollectionViewLayoutAttributes )finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath )itemIndexPath;

本Demo的代碼雖然子類化了UICollectionViewLayout,但是主要是用于自定義插入刪除動畫悯周,所以本段沒什么代碼展示粒督。


UICollectionView插入刪除的操作及動畫

插入刪除的操作

添加在哪觸發(fā):

    UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithTitle:@"添加"
                                                                style:UIBarButtonItemStylePlain
                                                               target:self
                                                               action:@selector(addItemBtnClick:)];
    
    self.navigationItem.rightBarButtonItem = btnItem;

添加的實現(xiàn):

// 添加(插入item)
- (void)addItemBtnClick:(UIBarButtonItem *)btnItem
{
    [_collectionView performBatchUpdates:^{
        // 構(gòu)造一個indexPath
        NSIndexPath *indePath = [NSIndexPath indexPathForItem:_section0Array.count inSection:0];
        [_collectionView insertItemsAtIndexPaths:@[indePath]]; // 然后在此indexPath處插入給collectionView插入一個item
        [_section0Array addObject:@"x"]; // 保持collectionView的item和數(shù)據(jù)源一致
    } completion:nil];
}

因為是練習(xí)Demo,所以暫時把刪除的觸發(fā)源寫在了長按某Item彈出菜單的copy按鈕里禽翼。實際中你可以自定義UICollectionViewCell屠橄,添加長按手勢,長按抖動出現(xiàn)叉號捐康,然后刪除等仇矾,隨你怎么做。

// copy and paste 的實現(xiàn)
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender
{
   if([NSStringFromSelector(action) isEqualToString:@"copy:"])
   {
//      NSLog(@"-------------執(zhí)行拷貝-------------");
       [_collectionView performBatchUpdates:^{
           [_section0Array removeObjectAtIndex:indexPath.row];
           [_collectionView deleteItemsAtIndexPaths:@[indexPath]];
       } completion:nil];
   }
   else if([NSStringFromSelector(action) isEqualToString:@"paste:"])
   {
       NSLog(@"-------------執(zhí)行粘貼-------------");
   }
}

插入刪除的動畫

上面已經(jīng)提到了在UICollectionViewLayout類中有兩個用于自定義動畫的方法解总,兩個方法分別表示動畫的起始狀態(tài)和終止?fàn)顟B(tài)贮匕,我們可以分別在方法里設(shè)置layoutAttrure來實現(xiàn)某種動畫效果。

蘋果選擇了一種安全的途徑去實現(xiàn)一個簡單的淡入淡出動畫作為所有布局的默認(rèn)動畫花枫。如果你想實現(xiàn)自定義動畫刻盐,最好的辦法是子類化 UICollectionViewFlowLayout 并且在適當(dāng)?shù)牡胤綄崿F(xiàn)你的動畫。

一般來說劳翰,我們對布局屬性從初始狀態(tài)到結(jié)束狀態(tài)進(jìn)行線性插值來計算 collection view 的動畫參數(shù)敦锌。然而,新插入或者刪除的元素并沒有最初或最終狀態(tài)來進(jìn)行插值佳簸。要計算這樣的 cells 的動畫乙墙,collection view 將通過 initialLayoutAttributesForAppearingItemAtIndexPath: 以及 finalLayoutAttributesForDisappearingItemAtIndexPath: 方法來詢問其布局對象,以獲取最初的和最后的屬性生均。蘋果默認(rèn)的實現(xiàn)中听想,對于特定的某個 indexPath,返回的是它的通常的位置马胧,但 alpha 值為 0.0汉买,這就產(chǎn)生了一個淡入或淡出動畫。

簡而言之佩脊,就是蘋果自帶了插入刪除時Item的淡入淡出的動畫蛙粘,若你想自定義更炫的動畫垫卤,就子類化UICollectionViewFlowLayout類,并重寫以下兩個方法:

// 初始狀態(tài)
- (nullable UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
    attr.center = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMaxY(self.collectionView.bounds));
    attr.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(0.2, 0.2), M_PI);

    return attr;
}


// 終結(jié)狀態(tài)
- (nullable UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
    UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
        attr.alpha = 0.0f;
    
    return attr;
}

insert&delete.gif

UICollectionView的轉(zhuǎn)場動畫

http://objccn.io/issue-12-5/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末出牧,一起剝皮案震驚了整個濱河市穴肘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌崔列,老刑警劉巖梢褐,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赵讯,居然都是意外死亡盈咳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門边翼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鱼响,“玉大人,你說我怎么就攤上這事组底≌苫” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵债鸡,是天一觀的道長江滨。 經(jīng)常有香客問我,道長厌均,這世上最難降的妖魔是什么唬滑? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮棺弊,結(jié)果婚禮上晶密,老公的妹妹穿的比我還像新娘。我一直安慰自己模她,他們只是感情好稻艰,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侈净,像睡著了一般尊勿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畜侦,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天元扔,我揣著相機(jī)與錄音,去河邊找鬼夏伊。 笑死,一個胖子當(dāng)著我的面吹牛吻氧,可吹牛的內(nèi)容都是我干的溺忧。 我是一名探鬼主播咏连,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鲁森!你這毒婦竟也來了祟滴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤歌溉,失蹤者是張志新(化名)和其女友劉穎垄懂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痛垛,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡草慧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了匙头。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漫谷。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蹂析,靈堂內(nèi)的尸體忽然破棺而出舔示,到底是詐尸還是另有隱情,我是刑警寧澤电抚,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布惕稻,位于F島的核電站,受9級特大地震影響蝙叛,放射性物質(zhì)發(fā)生泄漏俺祠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一甥温、第九天 我趴在偏房一處隱蔽的房頂上張望锻煌。 院中可真熱鬧,春花似錦姻蚓、人聲如沸宋梧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捂龄。三九已至,卻和暖如春加叁,著一層夾襖步出監(jiān)牢的瞬間倦沧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工它匕, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留展融,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓豫柬,卻偏偏與公主長得像告希,于是被迫代替她去往敵國和親扑浸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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

  • 翻譯自“Collection View Programming Guide for iOS” 0 關(guān)于iOS集合視...
    lakerszhy閱讀 3,866評論 1 22
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫燕偶、插件喝噪、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,105評論 4 62
  • 父類:NSObject UICollectionViewLayout是抽象基類,你可以使用它的子類來生成對coll...
    Shmily落墨閱讀 1,760評論 3 3
  • 濤哥離校的時候姑廉,守在火車站門口望著他的背影止不住地哭缺亮;慧姐離校的時候,擁別在車門口硬生生憋著沒哭桥言;蘇乾今天也離...
    sunrise421閱讀 352評論 0 1
  • 圖文/無為跑者 端午首日主義真萌踱, 戶外運(yùn)動一時辰。 身輕如燕氣色佳号阿, 發(fā)浸衣透濕渾身并鸵。
    最家游閱讀 354評論 32 27