自定義布局和自定義流水布局(CollectionViewLayout和CollectionViewFlowLayout)


  • 自定義流水布局--CollectionViewFlowLayout---水平布局實(shí)現(xiàn)一個(gè)相冊(cè)功能


  • 在UIScrollView的基礎(chǔ)上進(jìn)行循環(huán)利用
    • 那怎么去做循環(huán)利用呢?
  • 第一種方案:
    • 實(shí)時(shí)監(jiān)控ScrollView的滾動(dòng),一旦有一個(gè)家伙離開(kāi)屏幕,我們就把它放進(jìn)一個(gè)數(shù)組或者是集合里面去,到時(shí)候我要用,我就把它拿過(guò)去用
    • 但是這個(gè)是很麻煩的,因?yàn)槟憧偸堑门袛嗨袥](méi)有離開(kāi)屏幕
  • 第二種方案:
    • 用蘋(píng)果自帶的幾個(gè)類:TableView或者是CollectionView
    • 因?yàn)樗鼈儽緛?lái)就具備循環(huán)利用的功能
    • 但是TableView一看就不符合要求,因?yàn)樗J(rèn)就是上下豎直滾動(dòng),不是左右水平滾動(dòng)
      • 當(dāng)然我們也可以用非主流的方式,讓TableView實(shí)現(xiàn)水平滾動(dòng)
      • 讓TableView的Transform來(lái)個(gè)90°,讓它里面所有的cell也翻個(gè)90°,都轉(zhuǎn)過(guò)來(lái)。但這種做法有點(diǎn)奇葩,開(kāi)發(fā)中還是不要這么搞
      • 所以我們可以用CollectionView
    • CollectionView在我們的印象中是展示像那種九宮格的樣子,而且也是上下豎直滾動(dòng)
    • 但是CollectionView和TableView的區(qū)別就是:
      • CollectionView它默認(rèn)就支持水平滾動(dòng),你只要修改它一個(gè)屬性為水平方向就行了。而TableView默認(rèn)支持豎直滾動(dòng),沒(méi)有屬性去支持它水平滾動(dòng),除非你去搞一些非主流的做法

  • CollectionView一定要傳一個(gè)不空的Layout那個(gè)參數(shù),因?yàn)槟J(rèn)的布局是九宮格,它按這種方式排的原因是它有一個(gè)流水布局。正因?yàn)榻o它傳了一個(gè)流水布局,所以它就一行滿了,就流向下一行,流水一樣流下去流過(guò)來(lái)
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:[UICollectionViewlayout alloc] init]];
  • 數(shù)據(jù)源方法 - <UICollectionViewDataSource>
  • numberOfItemsInSection是告訴它一組有多少個(gè)格子
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 50 ;
}
  • cellForItemAtIndexPath告訴它每個(gè)格子長(zhǎng)出來(lái)是怎樣的一個(gè)cell,因?yàn)槊總€(gè)格子都是一個(gè)CollectionViewCell
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 先要注冊(cè)
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYCellId forIndexPath:indexPath];
    cell.backgroundColor = [UIColor orangeColor]

    return cell;
}
  • TableView和CollectionView的排布有很大的區(qū)別
    • TableView的排布是一行一行往下排布,而CollectionView的排布是完全取決于Layout,也就是說(shuō),你傳給它的Layout不一樣,它的排布就不一樣。它的布局決定了cell的排布
    • 也就是說(shuō),今后你想要CollectionView的cell排布豐富多彩,你只需要改變它的布局就行了
  • scrollDirection決定了它的滾動(dòng)方向,設(shè)置它滾動(dòng)的方向?yàn)樗?/li>
    // 水平滾動(dòng)
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  • itemSize決定了CollectionView布局的里面的cell的大小
    layout.itemSize = CGSizeMake(100, 100);
  • 你將CollectionView高度改小點(diǎn),比如200,那么你的高度不夠顯示兩排,就會(huì)如下顯示:
  • 而且你會(huì)發(fā)現(xiàn)不用擔(dān)心循環(huán)利用的問(wèn)題,CollectionView內(nèi)部已經(jīng)幫你做好了

  • 我們現(xiàn)在已經(jīng)實(shí)現(xiàn)流水布局水平滾動(dòng),而且做好了循環(huán)利用。如果要做一層改進(jìn),那么我們就要自定義布局,自己來(lái)寫(xiě)一套布局,所以現(xiàn)在我們繼承于UICollectionViewFlowLayout
  • 我們要自定義CollectionView的布局有兩種方案
    • 1.繼承UICollectionViewLayout
      • 一般是繼承于UICollectionViewLayout就行了
      • 而且UICollectionViewFlowLayout繼承于UICollectionViewLayout
      • 但是如果你自定義繼承于UICollectionViewLayout,代表著你沒(méi)有流水布局功能,也就是在你不想要流水布局功能的時(shí)候就選擇繼承UICollectionViewLayout
    • 2.繼承UICollectionViewFlowLayout

**四 **

  • 所以我們自定義流水布局CYLineLayout
  • 在CYLineLayout.h文件中
#import <UIKit/UIKit.h>

@interface CYLineLayout : UICollectionViewFlowLayout

@end

  • 在CYLineLayout.m文件中重寫(xiě)某些方法去實(shí)現(xiàn):
    • 1.cell的放大與縮小
    • 2.停止?jié)L動(dòng)的時(shí)候:cell居中
  • 進(jìn)入頭文件可以發(fā)現(xiàn)要重寫(xiě)的一些方法

  • UICollectionViewLayoutAttributes
    • 1.它是描述布局屬性的
    • 2.一個(gè)cell對(duì)應(yīng)一個(gè)UICollectionViewLayoutAttributes對(duì)象
    • 3.UICollectionViewLayoutAttributes對(duì)象決定了cell的展示樣式(frame)說(shuō)白了就是決定你的cell擺在哪里,怎么去擺


  • layoutAttributesForElementsInRect這個(gè)方法的返回值是一個(gè)數(shù)組(數(shù)組里面存放著rect范圍內(nèi)所有元素的布局屬性)
  • 這個(gè)方法的返回值決定了rect范圍內(nèi)所有元素的排布(frame)
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // 獲得super已經(jīng)計(jì)算好的布局屬性(在super已經(jīng)算好的基礎(chǔ)上,再去做一些改進(jìn))
    NSArray *array = [super layoutAttributesForElementsInRect:rect];

    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;

    // 在原有布局屬性的基礎(chǔ)上進(jìn)行微調(diào)
    for (UICollectionViewLayoutAttributes *attrs in array) {
        // cell的中心點(diǎn)x和collectionView最中心點(diǎn)的x值 的間距
        CGFloat delta = ABS(attrs.center.x - centerX);

        // 根據(jù)間距值計(jì)算cell的縮放比例
        CGFloat scale = 1 - delta / self.collectionView.frame.size.width;

        // 設(shè)置縮放比例
        attrs.transform = CGAffineTransformMakeScale(scale, scale);
    }

    return array;
}

  • 計(jì)算collectionView中心點(diǎn)的x值
    • 要記住collectionView的坐標(biāo)原點(diǎn)是以內(nèi)容contentSize的原點(diǎn)為原點(diǎn)
    • 計(jì)算collectionView中心點(diǎn)的x值,千萬(wàn)不要用collectionView的寬度除以2。而是用collectionView的偏移量加上collectionView寬度的一半
    • 坐標(biāo)原點(diǎn)弄錯(cuò)了就沒(méi)有可比性了,因?yàn)楹竺嬉袛郼ell的中心點(diǎn)與collectionView中心點(diǎn)的差值
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;

  • cell的中心點(diǎn)x 和CollectionView最中心點(diǎn)的x值 的間距
     CGFloat delta = ABS(attrs.center.x - centerX);
     ABS(A)
     // 表示取絕對(duì)值 

  • 我們?cè)俑鶕?jù)間距值delta去算cell的縮放比例scale
    • 間距值delta和縮放比例scale是成反比的
    • 間距值delta的范圍為0--self.collectionView.frame.size.width * 0.5
CGFloat scale = 1 - delta / self.collectionView.frame.size.width;
// 用1-(),是因?yàn)殚g距值delta和縮放比例scale是成反比的
  • 設(shè)置縮放比例
attrs.transform = CGAffineTransformMakeScale(scale, scale);

  • 但是設(shè)置后你會(huì)發(fā)現(xiàn)基本沒(méi)啥反應(yīng),顯示還亂七八糟的,這是什么原因呢?
    • 我們是想要稍微動(dòng)一下就修改一下,但是現(xiàn)在沒(méi)法達(dá)到我動(dòng)一下就根據(jù)最新的中心點(diǎn)X來(lái)再算一遍一邊比例。沒(méi)有實(shí)現(xiàn)這個(gè)代碼
    • 因?yàn)檫@里還需要實(shí)現(xiàn)一個(gè)方法
  • 這個(gè)方法是shouldInvalidateLayoutForBoundsChange: 它的特點(diǎn)是:
    • 默認(rèn)return NO
    • 當(dāng)collectionView的顯示范圍發(fā)生改變的時(shí)候,判斷是否需要重新刷新布局
    • 一旦重新刷新布局,就會(huì)重新調(diào)用下面的方法:
      • 1.prepareLayout
      • 2.layoutAttributesForElementsInRect:方法
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
  
}
  • 這樣之后,你會(huì)發(fā)現(xiàn),你稍微挪一下,它就重新算一遍,比例就會(huì)縮放, 達(dá)到了我們的要求
  • 而且非常流暢,因?yàn)樗醒h(huán)利用

  • 還要實(shí)現(xiàn)一個(gè)方法:targetContentOffsetForProposedContentOffset:()方法。它的返回值,就決定了collectionView停止?jié)L動(dòng)時(shí)的偏移量
  • 這個(gè)方法在你手離開(kāi)屏幕之前會(huì)調(diào)用,也就是cell即將停止?jié)L動(dòng)的時(shí)候 (記住這一點(diǎn))
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    // 計(jì)算出最終顯示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;

    // 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];

    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;

    // 存放最小的間距值
    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
            minDelta = attrs.center.x - centerX;
        }
    }

    // 修改原有的偏移量
    proposedContentOffset.x += minDelta;
    return proposedContentOffset;
}

  • 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
  • 這里為什么不用self
    • 因?yàn)槿绻{(diào)self,又會(huì)來(lái)到layoutAttributesForElementsInRect:()方法的for循環(huán)中, 將transform再算一遍。而我們只想要拿到中心點(diǎn)X值??扛割惥托辛?/li>
    • 我們調(diào)super這個(gè)方法,因?yàn)樗?dāng)時(shí)已經(jīng)算好了cell的中心點(diǎn)等X的值了。所以這里調(diào)super可能更好一點(diǎn)

  • 計(jì)算collectionView最中心點(diǎn)的x值
 CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
- 這里為什么不按前面
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;

來(lái)算呢?

  • 因?yàn)閠argetContentOffsetForProposedContentOffset:()方法在你手離開(kāi)屏幕之前會(huì)調(diào)用,也就是cell即將停止?jié)L動(dòng)的時(shí)候,這個(gè)時(shí)候我們要算的是最后停下來(lái)偏移量。
  • 假如我們用力往左邊一甩,你的手已經(jīng)離開(kāi),算的偏移量是你手離開(kāi)時(shí)候的偏移量,而不是我們最終的偏移量,也就是說(shuō)這么算的話,我們就算錯(cuò)了
  • 你是應(yīng)該拿到最終停下來(lái)的cell和CollectionView的中心點(diǎn)的X值進(jìn)行比較的。所以你應(yīng)該最終的值,而不是手松開(kāi)的那一刻的偏移量的值
    • 那我們?cè)趺粗朗炙砷_(kāi)的那一刻最終的偏移量X的值呢?
      • 這個(gè)方法返回的參數(shù)(CGPoint)proposedContentOffset,這是它本應(yīng)該停留的位置,最終停留的的值。而(CGPoint)targetContentOffsetForProposedContentOffset:這個(gè)是你最終返回的值,也就是你要它停留到哪兒的值(這個(gè)參數(shù)決定你要cell最后停留在哪兒)

  • 同上面可知,我們最后拿到的矩形框也是不能亂傳的,也是要拿到最終的哪一個(gè)矩形框(不明白,就想像一下,你往左邊或者右邊用手指一甩的時(shí)候,手離開(kāi)的時(shí)候是一個(gè)值,最終停下來(lái)是一個(gè)值,而現(xiàn)在我們需要的是最終的值)
    // 計(jì)算出最終顯示的矩形框 
    CGRect rect; rect.origin.y = 0; rect.origin.x = proposedContentOffset.x; rect.size = self.collectionView.frame.size;


  • 然后我們要找最短的偏移量,找到它,然后就讓他偏移?它的那個(gè)值,讓它的中心點(diǎn)回到collectionView的中心點(diǎn),也就是說(shuō)重合。這樣就實(shí)現(xiàn)了不管你怎么去甩,等cell停下來(lái)的時(shí)候。都會(huì)有一個(gè)cell它會(huì)停留在矩形框CollectionView的中心
    // 存放最小的間距值
         CGFloat minDelta = MAXFLOAT; 
         for (UICollectionViewLayoutAttributes *attrs in array) {
               if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {                     
              minDelta = attrs.center.x - centerX; 
              }
       } 
        // 修改原有的偏移量 
        proposedContentOffset.x += minDelta; 
        return proposedContentOffset;
  • 一開(kāi)始先保證minDelta是最大的,保證誰(shuí)都比你小。 第一次算出來(lái)的絕對(duì)值就肯定比你小,然后把它賦值給你minDelta。?這樣就算出來(lái)了最小的間距值
  • 算出來(lái)最小間距值后,你通過(guò)分析應(yīng)該會(huì)發(fā)現(xiàn),不管是往左偏還是往右偏,要想讓cell回到中心點(diǎn),最后你的偏移量應(yīng)該是用:你本來(lái)應(yīng)該 的偏移量+(cell的中心點(diǎn)X值—collectionView中心點(diǎn)X值)
  • 所以上面在比較的時(shí)候用絕對(duì)值,計(jì)算的時(shí)候不用絕對(duì)值,minDelta最后就有正數(shù)也有負(fù)數(shù)
  • 修改后讓它回到中間
  • 最后不管你怎么滑,它都會(huì)停在中間

  • 有一個(gè)小缺陷,你會(huì)發(fā)現(xiàn),一打開(kāi)程序,你往左或往右滑到最左或者最右的時(shí)候,cell總是默認(rèn)粘著邊上,這個(gè)不太和諧,我們需要它距離左右兩邊都有一個(gè)距離,那我們?cè)撛趺醋瞿兀?
    • 這就是讓我們把所有的cell,讓它們往右邊或者左邊挪一段距離,所以就增加內(nèi)邊距就可以了。怎么添加內(nèi)邊距呢?
      • collectionView是繼承ScrollView的,所以設(shè)置它的ContentInset就可以了
      • 還一種方法通過(guò)這個(gè)布局它本來(lái)就有一個(gè)屬性sectionInset ,這本來(lái)就是來(lái)控制內(nèi)邊距的,控制整個(gè)布局的。而且這個(gè)屬性只需要設(shè)置一次
  • 這里有一個(gè)?給collectionView專門(mén)用來(lái)布局的方法---prepareLayout,這里一般是做初始化操作
/**
 * 用來(lái)做布局的初始化操作(不建議在init方法中進(jìn)行布局的初始化操作--可能布局還未加到View中去,就會(huì)返回為空)
 */
- (void)prepareLayout
{
    [super prepareLayout];

    // 設(shè)置內(nèi)邊距
    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}


  • 總的來(lái)說(shuō)我們?nèi)粢^承自這個(gè)流水布局來(lái)實(shí)現(xiàn)這個(gè)功能的話,肯定是要重寫(xiě)一些方法,告訴它一些內(nèi)部的行為,它才知道怎么去顯示那個(gè)東西,我們用了一下的方法:
    • 我們首先得實(shí)現(xiàn)prepareLayout方法,做一些初始化
    • 然后,我們實(shí)現(xiàn)layoutAttributesForElementsInRect:方法。目的是拿出它計(jì)算好的布局屬性來(lái)做一個(gè)微調(diào),這樣可以導(dǎo)致我們的cell可以變大或者變小
    • 然后實(shí)現(xiàn)targetContentOffsetForProposedContentOffset:方法。它的目的是告訴當(dāng)我手松開(kāi),cell停止?jié)L動(dòng)的時(shí)候,他應(yīng)該去哪兒,所以這個(gè)方法就決定了collectionView停止?jié)L動(dòng)時(shí)的偏移量
    • 最后shouldInvalidateLayoutForBoundsChange:這個(gè)方法的價(jià)值就是告訴它你只要稍微往左或者往右挪一下,你就重新刷新,只要你重新刷新,它就會(huì)重新根據(jù)你cell的中心點(diǎn)的X值距離你collectionView中心點(diǎn)的X值來(lái)決定你的縮放比例。這樣就保證了我們每動(dòng)一點(diǎn)點(diǎn),比例都在變,所以我們要?jiǎng)右幌滤⑿乱幌隆R簿褪钱?dāng)collectionView的顯示范圍發(fā)生改變的時(shí)候,是否需要重新刷新布局,一旦重新刷新布局,就會(huì)重新調(diào)用下面的方法:1.prepareLayout2.layoutAttributesForElementsInRect:方法
  • 關(guān)于做這個(gè)效果有一個(gè)挺牛逼的三方框架:iCarousel大家可以參考一下

  • 在CYLineLayout.h文件中
#import <UIKit/UIKit.h>

@interface CYLineLayout : UICollectionViewFlowLayout

@end
  • 在CYLineLayout.h文件中
#import "CYLineLayout.h"

@implementation CYLineLayout

- (instancetype)init
{
    if (self = [super init]) {
    }
    return self;
}

/**
 * 當(dāng)collectionView的顯示范圍發(fā)生改變的時(shí)候,是否需要重新刷新布局
 * 一旦重新刷新布局,就會(huì)重新調(diào)用下面的方法:
 1.prepareLayout
 2.layoutAttributesForElementsInRect:方法
 */
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}

/**
 * 用來(lái)做布局的初始化操作(不建議在init方法中進(jìn)行布局的初始化操作)
 */
- (void)prepareLayout
{
    [super prepareLayout];

    // 水平滾動(dòng) 
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    // 設(shè)置內(nèi)邊距
    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}

/**
 UICollectionViewLayoutAttributes *attrs;
 1.一個(gè)cell對(duì)應(yīng)一個(gè)UICollectionViewLayoutAttributes對(duì)象
 2.UICollectionViewLayoutAttributes對(duì)象決定了cell的frame
 */
/**
 * 這個(gè)方法的返回值是一個(gè)數(shù)組(數(shù)組里面存放著rect范圍內(nèi)所有元素的布局屬性)
 * 這個(gè)方法的返回值決定了rect范圍內(nèi)所有元素的排布(frame)
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    // 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect] ;

    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;

    // 在原有布局屬性的基礎(chǔ)上,進(jìn)行微調(diào)
    for (UICollectionViewLayoutAttributes *attrs in array) {
        // cell的中心點(diǎn)x 和 collectionView最中心點(diǎn)的x值 的間距
        CGFloat delta = ABS(attrs.center.x - centerX);

        // 根據(jù)間距值 計(jì)算 cell的縮放比例
        CGFloat scale = 1 - delta / self.collectionView.frame.size.width;

        // 設(shè)置縮放比例
        attrs.transform = CGAffineTransformMakeScale(scale, scale);
    }
        return array;
}

/**
 * 這個(gè)方法的返回值,就決定了collectionView停止?jié)L動(dòng)時(shí)的偏移量

 */
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    // 計(jì)算出最終顯示的矩形框
    CGRect rect;
    rect.origin.y = 0;
    rect.origin.x = proposedContentOffset.x;
    rect.size = self.collectionView.frame.size;

    // 獲得super已經(jīng)計(jì)算好的布局屬性
    NSArray *array = [super layoutAttributesForElementsInRect:rect];

    // 計(jì)算collectionView最中心點(diǎn)的x值
    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;

    // 存放最小的間距值
    CGFloat minDelta = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (ABS(minDelta) > ABS(attrs.center.x - centerX)) {
            minDelta = attrs.center.x - centerX;
        }
    }

    // 修改原有的偏移量
    proposedContentOffset.x += minDelta;
    return proposedContentOffset;
}

@end

  • 假如我們要監(jiān)聽(tīng)cell的點(diǎn)擊,要怎么辦呢?上面這講的這些都和CollectionViewCell的點(diǎn)擊沒(méi)有關(guān)系,只是和布局有關(guān)。監(jiān)聽(tīng)CollectionViewCell的點(diǎn)擊和CollectionViewCell的布局沒(méi)有任何關(guān)系,布局只負(fù)責(zé)展示,格子里面是什么內(nèi)容,還是取決于cell
  • 布局的作用僅僅是控制cell的排布
    • 控制器先成為CollectionViewCell的代理:UICollectionViewDelegate
  • 現(xiàn)在要把數(shù)據(jù)填充上去,讓它顯示相冊(cè)了,所以自定義CollectionViewCell--CYPhotoCell,由于里面是固定死的,所以加一個(gè)Xib文件,里面加一個(gè)ImageView,拖線給一個(gè)屬性,給ImageView一個(gè)標(biāo)識(shí)photo
  • 給cell里面的相片加上一個(gè)相冊(cè)相框的效果--兩種方案:
    • 第一種方案:在Xib的ImageView的布局上下左右都給一個(gè)10的間距,給一個(gè)white的背景顏色
    • 第二種方案:給我們的ImageView加一個(gè)圖層就可以了
- (void)awakeFromNib {
       self.imageView.layer.borderColor = [UIColor whiteColor].CGColor; 
       self.imageView.layer.borderWidth = 10;
 }
  • 在CYPhotoCell.h文件中
#import <UIKit/UIKit.h>

@interface CYPhotoCell : UICollectionViewCell
/** 圖片名 */
@property (nonatomic, copy) NSString *imageName;
@end
  • 在CYPhotoCell.m文件中
#import "CYPhotoCell.h"

@interface CYPhotoCell()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end

@implementation CYPhotoCell

- (void)awakeFromNib {
    self.imageView.layer.borderColor = [UIColor whiteColor].CGColor;
    self.imageView.layer.borderWidth = 10;
}

- (void)setImageName:(NSString *)imageName
{
    _imageName = [imageName copy];

    self.imageView.image = [UIImage imageNamed:imageName];
}

@end
  • 在ViewController.m文件中
#import "ViewController.h"
#import "CYLineLayout.h"
#import "CYPhotoCell.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@end

@implementation ViewController

static NSString * const CYPhotoId = @"photo";

- (void)viewDidLoad {
    [super viewDidLoad];

    // 創(chuàng)建布局
    CYLineLayout *layout = [[CYLineLayout alloc] init];
    layout.itemSize = CGSizeMake(100, 100);

    // 創(chuàng)建CollectionView
    CGFloat collectionW = self.view.frame.size.width;
    CGFloat collectionH = 200;
    CGRect frame = CGRectMake(0, 150, collectionW, collectionH);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [self.view addSubview:collectionView];

    // 注冊(cè)
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYPhotoCell class]) bundle:nil] forCellWithReuseIdentifier:CYPhotoId];
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CYPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYPhotoId forIndexPath:indexPath];

    cell.imageName = [NSString stringWithFormat:@"%zd", indexPath.item + 1];

    return cell;
}

#pragma mark - <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"------%zd", indexPath.item);
}
@end
  • 最后就實(shí)現(xiàn)了:



自定義流水布局

  • 自定義布局 - 繼承UICollectionViewFlowLayout

  • 重寫(xiě)prepareLayout方法

    • 作用:
      - 在這個(gè)方法中做一些初始化操作
    • 注意:
      - 一定要調(diào)用[super prepareLayout]
  • 重寫(xiě)layoutAttributesForElementsInRect:方法

    • 作用:
      - 這個(gè)方法的返回值是個(gè)數(shù)組
      - 這個(gè)數(shù)組中存放的都是UICollectionViewLayoutAttributes對(duì)象
      - UICollectionViewLayoutAttributes對(duì)象決定了cell的排布方式(frame等)
  • 重寫(xiě)shouldInvalidateLayoutForBoundsChange:方法

    • 作用:
      - 如果返回YES,那么collectionView顯示的范圍發(fā)生改變時(shí),就會(huì)重新刷新布局
    • 一旦重新刷新布局,就會(huì)按順序調(diào)用下面的方法:
      - prepareLayout
      - layoutAttributesForElementsInRect:
  • 重寫(xiě)targetContentOffsetForProposedContentOffset:方法

    • 作用:
      - 返回值決定了collectionView停止?jié)L動(dòng)時(shí)最終的偏移量(contentOffset)
    • 參數(shù):
      - proposedContentOffset:原本情況下,collectionView停止?jié)L動(dòng)時(shí)最終的偏移量
      - velocity:滾動(dòng)速率,通過(guò)這個(gè)參數(shù)可以了解滾動(dòng)的方向(根據(jù)X和Y的正負(fù))





自定義布局--CollectionViewLayout--格子布局

  • 分析一下這個(gè)布局的排布是有規(guī)律的:
    • 這里的相冊(cè)布局和上面的流水布局不同
    • 我們較上面的不需要更改太多東西,只是修改它的布局方式就行了
    • 六個(gè)為一組
    • 對(duì)應(yīng)cell相差兩個(gè)高度


  • 一個(gè)這樣的布局如何實(shí)現(xiàn)?
    • 首先這里不不好用流水布局,流水布局的ItemSize是一樣大的
    • 肯定也牽扯到了循環(huán)利用,所以仍然用CollectionView,?所以就用一個(gè)?最根的布局--CollectionViewLayout
    • CollectionViewLayout它不像流水布局,內(nèi)部沒(méi)有任何方法給你去排,所以你只有繼承自它,然后自己去寫(xiě)一套排布方式,排布是由我們來(lái)算
    • 將上面文件中的CYLineLayout刪除,New一個(gè)File--CYGridLayout繼承自CollectionViewLayout

    // 創(chuàng)建UICollectionViewLayoutAttributes
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
  • 說(shuō)白了我這個(gè)UICollectionViewLayoutAttributes是描述一個(gè)cell用的
  • indexPath代表了對(duì)應(yīng)某個(gè)位置的cell,也就是說(shuō)我這個(gè)UICollectionViewLayoutAttributes是描述哪個(gè)位置的cell
  • 通過(guò)觀察可以發(fā)現(xiàn)規(guī)律


  • 在ViewController.m文件中修改一下collectionView的frame和布局
#import "ViewController.h"
#import "CYGridLayout.h"
#import "CYPhotoCell.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@end

@implementation ViewController

static NSString * const CYPhotoId = @"photo";

- (void)viewDidLoad {
    [super viewDidLoad];

    // 創(chuàng)建布局
    CYGridLayout *layout = [[CYGridLayout alloc] init];

    // 創(chuàng)建CollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [self.view addSubview:collectionView];

    // 注冊(cè)
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYPhotoCell class]) bundle:nil] forCellWithReuseIdentifier:CYPhotoId];
}
  • CYGridLayout里面去實(shí)現(xiàn)collectionView具體的布局
  • 在CYGridLayout.m文件中
#import "CYGridLayout.h"

@interface CYGridLayout()
/** 所有的布局屬性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation CYGridLayout

- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

- (void)prepareLayout
{
    [super prepareLayout];

    [self.attrsArray removeAllObjects];

    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i++) {
        // 創(chuàng)建UICollectionViewLayoutAttributes
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

        // 設(shè)置布局屬性
        CGFloat width = self.collectionView.frame.size.width * 0.5;
        if (i == 0) {
            CGFloat height = width;
            CGFloat x = 0;
            CGFloat y = 0;
            attrs.frame = CGRectMake(x, y, width, height);
        } else if (i == 1) {
            CGFloat height = width * 0.5;
            CGFloat x = width;
            CGFloat y = 0;
            attrs.frame = CGRectMake(x, y, width, height);
        } else if (i == 2) {
            CGFloat height = width * 0.5;
            CGFloat x = width;
            CGFloat y = height;
            attrs.frame = CGRectMake(x, y, width, height);
        } else if (i == 3) {
            CGFloat height = width * 0.5;
            CGFloat x = 0;
            CGFloat y = width;
            attrs.frame = CGRectMake(x, y, width, height);
        } else if (i == 4) {
            CGFloat height = width * 0.5;
            CGFloat x = 0;
            CGFloat y = width + height;
            attrs.frame = CGRectMake(x, y, width, height);
        } else if (i == 5) {
            CGFloat height = width;
            CGFloat x = width;
            CGFloat y = width;
            attrs.frame = CGRectMake(x, y, width, height);
        } else {
            UICollectionViewLayoutAttributes *lastAttrs = self.attrsArray[i - 6];
            CGRect lastFrame = lastAttrs.frame;
            lastFrame.origin.y += 2 * width;
            attrs.frame = lastFrame;
        }

        // 添加UICollectionViewLayoutAttributes
        [self.attrsArray addObject:attrs];
    }
}

  • 運(yùn)行程序:
  • 你會(huì)發(fā)現(xiàn)無(wú)法使它往上滾動(dòng),這是為啥呢?
    • 因?yàn)槟悻F(xiàn)在時(shí)繼承自最根本的布局CollectionViewLayout,很多東西是得自己去設(shè)置了才會(huì)有,來(lái)到頭文件,你會(huì)發(fā)現(xiàn)
    • 要重寫(xiě)它的(CGSize)collectionViewContentSize方法,告訴它你這個(gè)CollectionView的內(nèi)容尺寸,來(lái)決定它怎么滾。所以你現(xiàn)在無(wú)法滾動(dòng)是因?yàn)镃ollectionView的ContentSize沒(méi)有確定
/**
 * 返回collectionView的內(nèi)容大小
 */
- (CGSize)collectionViewContentSize
{
    int count = (int)[self.collectionView numberOfItemsInSection:0];
    int rows = (count + 3 - 1) / 3;
    CGFloat rowH = self.collectionView.frame.size.width * 0.5;
    return CGSizeMake(0, rows * rowH);
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

  • 這里在性能優(yōu)化上是還有點(diǎn)小問(wèn)題的,因?yàn)槲覀円豢跉獍阉袞|西都算完了。你如果覺(jué)得費(fèi)時(shí),完全可以把計(jì)算放在子線程中,然后返回到主線程刷新UI(CollectionViewLayout布局中有一個(gè)刷新方法,你調(diào)一下就行了)
  • 計(jì)算不是重點(diǎn),你是可以總結(jié)出計(jì)算的規(guī)律的。重點(diǎn)是:繼承自CollectionViewLayout你需要注意什么?
    • 1.一旦你重寫(xiě)了layoutAttributesForElementsInRect這個(gè)方法,就意味著所有東西你得自己寫(xiě)了,你的Attributes對(duì)象得自己創(chuàng)建了,因?yàn)樗母割惒粫?huì)幫你創(chuàng)建
    • 2.一旦你繼承自CollectionViewLayout,意味著你這個(gè)collectionViewContentSize都得告訴它了,這個(gè)是得你自己去算的
    • 3.如果你是希望一口氣把所有東西算完,不希望它在滾動(dòng)過(guò)程中再算,你可以在prepareLayout方法里面先算清楚,算完后盡管它傳的矩形框都不一樣,但是我返回的還是同一份。
  • ?這里給?出一個(gè)思想:
    • 以后,你凡事?tīng)砍兜絻?nèi)容是很多很多的,你想做什么循環(huán)利用,而且布局又亂七八糟的,我們用CollectionViewLayout就可以了。我們只有繼承自這個(gè)CollectionViewLayout,然后我們實(shí)現(xiàn)layoutAttributesForElementsInRect這個(gè)方法,在那里去告訴它,你的cell怎么去排。并且繼承自CollectionViewLayout,意味著很多東西都要重寫(xiě),如:collectionViewContentSize
  • 這樣就實(shí)現(xiàn)了:







自定義布局--CollectionViewLayout--布局之間的切換

  • 要求:
    • 實(shí)現(xiàn)一個(gè)環(huán)形布局和水平布局的相冊(cè),點(diǎn)擊屏幕能夠進(jìn)行不同布局之間的切換
    • 點(diǎn)擊cell的時(shí)候可以刪除cell
  • 首先通過(guò)分析,在上面第一個(gè)案例的基礎(chǔ)上,再添加一個(gè)環(huán)形布局--CYCircleLayout,肯定也是只能繼承自CollectionViewLayout
  • 在這里CYCircleLayout里面就只需要實(shí)現(xiàn)prepareLayout方法和layoutAttributesForElementsInRect方法,不需再要重寫(xiě)實(shí)現(xiàn)collectionViewContentSize的方法,因?yàn)樗恍枰獫L動(dòng),所以CollectionViewLayout里面所有方法的實(shí)現(xiàn)是看你的需求的
#import "CYCircleLayout.h"

@interface CYCircleLayout()
/** 布局屬性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation CYCircleLayout

- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

- (void)prepareLayout
{
    [super prepareLayout];

    [self.attrsArray removeAllObjects];

    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attrsArray addObject:attrs];
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

  • 我們可以看出,每個(gè)相片cell的中心點(diǎn)都在一個(gè)圓上,所以我們要將它擺正,肯定不是設(shè)置它們的frame,而是去設(shè)置它c(diǎn)enter這個(gè)值,我只要保證它的center那個(gè)值在那個(gè)圓上就可以了
  • 也就是說(shuō)我們要算出每個(gè)相片cell的中心點(diǎn)的X和Y值,通過(guò)中心點(diǎn)來(lái)布局它,而不是通過(guò)frame的original的X和Y(這樣太麻煩,不好算)
  • 這里我們只要確定圓心就好算了
    • 圓心(X和Y值分別是CollectionView寬度和高度的一半)
    • 而且每張相片的中心點(diǎn)距離圓心的距離為半徑
    • 你會(huì)發(fā)現(xiàn)每個(gè)相片cell的中心點(diǎn)的X,Y和圓心的X,Y之間的差值是有規(guī)律的:
      • Y值--圓心點(diǎn)的Y值-(Y*cosa)= cell的Y值,X值同樣道理去算
      • 角度a的大小取決于cell的個(gè)數(shù)(假如20個(gè)cell--->a = 360° / 20)


  • 所以我們只要算出平分角度就行了
  • 比如說(shuō)第一個(gè)cell為索引0,角度就是0,第二個(gè)為索引1,角度就是a, 第三個(gè)為索引2,角度就是a2......第i個(gè)為索引i-1,角度就是a(i-1 )
  • 于是乎

  • 這里記住:如果你是繼承自CollectionViewLayout,如果你要換布局話,有一個(gè)方法是一定得實(shí)現(xiàn)的--layoutAttributesForItemAtIndexPath:方法。只有繼承CollectionViewLayout才需要,流水布局不需要,因?yàn)榱魉季謨?nèi)部早已經(jīng)幫你實(shí)現(xiàn)了這個(gè)方法
/**
 * 這個(gè)方法需要返回indexPath位置對(duì)應(yīng)cell的布局屬性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    CGFloat radius = 70;
    // 圓心的位置
    CGFloat oX = self.collectionView.frame.size.width * 0.5;
    CGFloat oY = self.collectionView.frame.size.height * 0.5;

    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    attrs.size = CGSizeMake(50, 50);
    if (count == 1) {
        attrs.center = CGPointMake(oX, oY);
    } else {
        CGFloat angle = (2 * M_PI / count) * indexPath.item;
        CGFloat centerX = oX + radius * sin(angle);
        CGFloat centerY = oY + radius * cos(angle);
        attrs.center = CGPointMake(centerX, centerY);
    }

    return attrs;
}
  • 點(diǎn)擊屏幕切換布局
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ([self.collectionView.collectionViewLayout isKindOfClass:[CYLineLayout class]]) {
        [self.collectionView setCollectionViewLayout:[[CYCircleLayout alloc] init] animated:YES];
    } else {
        CYLineLayout *layout = [[CYLineLayout alloc] init];
        layout.itemSize = CGSizeMake(100, 100);
        [self.collectionView setCollectionViewLayout:layout animated:YES];
    }
}
  • 點(diǎn)擊cell就把cell刪掉
    • 這里要注意的是:
      • 你要把cell刪掉了,對(duì)應(yīng)的模型或者說(shuō) 數(shù)據(jù)也是得改變的
  • 可變數(shù)組,先把所有圖片名放進(jìn)去
@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
/** collectionView */
@property (nonatomic, weak) UICollectionView *collectionView;
/** 數(shù)據(jù) */
@property (nonatomic, strong) NSMutableArray *imageNames;
@end

@implementation ViewController

static NSString * const CYPhotoId = @"photo";

- (NSMutableArray *)imageNames
{
    if (!_imageNames) {
        _imageNames = [NSMutableArray array];
        for (int i = 0; i<20; i++) {
            [_imageNames addObject:[NSString stringWithFormat:@"%zd", i + 1]];
        }
    }
    return _imageNames;
}

  • 數(shù)據(jù)源里面的東西也是得改變的
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.imageNames.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CYPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYPhotoId forIndexPath:indexPath];

    cell.imageName = self.imageNames[indexPath.item];

    return cell;
}
  • 你要把cell刪掉,也得保證把模型也刪掉了(不可能你cell刪掉了,數(shù)據(jù)還是這么多,那就出問(wèn)題了)
#pragma mark - <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self.imageNames removeObjectAtIndex:indexPath.item];
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
}
  • 刪除到最后一個(gè)的時(shí)候,讓最后一個(gè)cell的位置來(lái)到圓心
    if (count == 1) {
        attrs.center = CGPointMake(oX, oY);
    } else {
        CGFloat angle = (2 * M_PI / count) * indexPath.item;
        CGFloat centerX = oX + radius * sin(angle);
        CGFloat centerY = oY + radius * cos(angle);
        attrs.center = CGPointMake(centerX, centerY);
    }


  • 這樣所有的邏輯就理清楚了

  • 在CYCircleLayout.m文件中
#import "CYCircleLayout.h"

@interface CYCircleLayout()
/** 布局屬性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation CYCircleLayout

- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        _attrsArray = [NSMutableArray array];
    }
    return _attrsArray;
}

- (void)prepareLayout
{
    [super prepareLayout];

    [self.attrsArray removeAllObjects];

    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i < count; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
        [self.attrsArray addObject:attrs];
    }
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

/**
 * 這個(gè)方法需要返回indexPath位置對(duì)應(yīng)cell的布局屬性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    CGFloat radius = 70;
    // 圓心的位置
    CGFloat oX = self.collectionView.frame.size.width * 0.5;
    CGFloat oY = self.collectionView.frame.size.height * 0.5;

    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    attrs.size = CGSizeMake(50, 50);
    if (count == 1) {
        attrs.center = CGPointMake(oX, oY);
    } else {
        CGFloat angle = (2 * M_PI / count) * indexPath.item;
        CGFloat centerX = oX + radius * sin(angle);
        CGFloat centerY = oY + radius * cos(angle);
        attrs.center = CGPointMake(centerX, centerY);
    }

    return attrs;
}
@end
  • 在ViewController.m文件中
#import "ViewController.h"
#import "CYLineLayout.h"
#import "CYCircleLayout.h"
#import "CYPhotoCell.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
/** collectionView */
@property (nonatomic, weak) UICollectionView *collectionView;
/** 數(shù)據(jù) */
@property (nonatomic, strong) NSMutableArray *imageNames;
@end

@implementation ViewController

static NSString * const CYPhotoId = @"photo";

- (NSMutableArray *)imageNames
{
    if (!_imageNames) {
        _imageNames = [NSMutableArray array];
        for (int i = 0; i<20; i++) {
            [_imageNames addObject:[NSString stringWithFormat:@"%zd", i + 1]];
        }
    }
    return _imageNames;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // 創(chuàng)建布局
    CYCircleLayout *layout = [[CYCircleLayout alloc] init];

    // 創(chuàng)建CollectionView
    CGFloat collectionW = self.view.frame.size.width;
    CGFloat collectionH = 200;
    CGRect frame = CGRectMake(0, 150, collectionW, collectionH);
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [self.view addSubview:collectionView];
    self.collectionView = collectionView;

    // 注冊(cè)
    [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([CYPhotoCell class]) bundle:nil] forCellWithReuseIdentifier:CYPhotoId];

    // 繼承UICollectionViewLayout
    // 繼承UICollectionViewFlowLayout
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ([self.collectionView.collectionViewLayout isKindOfClass:[CYLineLayout class]]) {
        [self.collectionView setCollectionViewLayout:[[CYCircleLayout alloc] init] animated:YES];
    } else {
        CYLineLayout *layout = [[CYLineLayout alloc] init];
        layout.itemSize = CGSizeMake(100, 100);
        [self.collectionView setCollectionViewLayout:layout animated:YES];
    }
}


#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.imageNames.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CYPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CYPhotoId forIndexPath:indexPath];

    cell.imageName = self.imageNames[indexPath.item];

    return cell;
}

#pragma mark - <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    [self.imageNames removeObjectAtIndex:indexPath.item];
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
}
@end


  • 這樣就實(shí)現(xiàn)了


  • 如果覺(jué)得對(duì)你有幫助,?Give me a star

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,746評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,666評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,477評(píng)論 6 407
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,960評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,200評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,617評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,807評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,049評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,425評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,674評(píng)論 1 281
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,432評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,769評(píng)論 2 372

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