iOS -CustomCollectionViewFlowLayout優(yōu)先居左

** Demo_github**

圖片來自:[蓉er蓉er](http://www.reibang.com/u/904f3fc2ce35)

UICollectionViewLayout的功能為向UICollectionView提供布局信息威始,不僅包括cell的布局信息鼻吮,也包括追加視圖和裝飾視圖的布局信息车吹。
通常我們使用UICollectionViewLayout時是由系統(tǒng)幫助計算布局的,如圖:


系統(tǒng)幫助計算布局的Layout

這顯然不符合我們的需求醋闭。在系統(tǒng)的UICollectionViewLayout不能滿足我們的需求時窄驹,可實現(xiàn)一個自定義的繼承UICollectionViewLayout類CustomCollectionViewFlowLayout。
我們需要items優(yōu)先居左顯示证逻,并自動換行乐埠。如圖:


最終需要的效果圖

本文主要是講述CustomCollectionViewFlowLayout優(yōu)先居左排列的自定義布局。其實現(xiàn)的步驟如下:
首先自定義一個CustomCollectionViewFlowLayout繼承自UICollectionViewLayout類瑟曲。

#import <UIKit/UIKit.h>

@interface SkyLeftItemsCollectionViewFlowLayout : UICollectionViewFlowLayout<UICollectionViewDelegateFlowLayout>//繼承自UICollectionViewLayout類

@end
  • UICollectionView的結(jié)構(gòu)
Cells
Supplementary Views 追加視圖 (類似Header或者Footer)
Decoration Views 裝飾視圖 (用作背景展示)
  • UICollectionViewLayout是對collectionView的布局和行為進行描述的一個類。

在CustomCollectionViewFlowLayout.m中重載下列方法:

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
 1.返回rect中的所有的元素的布局屬性
 2.返回的是包含UICollectionViewLayoutAttributes的NSArray
 3.UICollectionViewLayoutAttributes可以是cell豪治,追加視圖或裝飾視圖的信息洞拨,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的UICollectionViewLayoutAttributes:
  1)layoutAttributesForCellWithIndexPath:
  2)layoutAttributesForSupplementaryViewOfKind:withIndexPath:
  3)layoutAttributesForDecorationViewOfKind:withIndexPath:
-(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath
返回對應(yīng)于indexPath的位置的cell的布局屬性

-(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath
返回對應(yīng)于indexPath的位置的追加視圖的布局屬性,如果沒有追加視圖可不重載

-(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath
返回對應(yīng)于indexPath的位置的裝飾視圖的布局屬性负拟,如果沒有裝飾視圖可不重載

UICollectionViewLayout中方法的調(diào)用順序:

  • 1 -(void)prepareLayout 設(shè)置layout的結(jié)構(gòu)和初始需要的參數(shù)等烦衣。
  • 2 -(CGSize) collectionViewContentSize 確定collectionView的所有內(nèi)容的尺寸。
  • 3 -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定掩浙。
  • 4 在需要更新layout時花吟,需要給當(dāng)前l(fā)ayout發(fā)送
  • 1 -invalidateLayout, 該消息會立即返回厨姚,并且預(yù)約在下一個loop的時候刷新當(dāng)前l(fā)ayout
  • 2 -prepareLayout衅澈,
  • 3 依次再調(diào)用-collectionViewContentSize和-layoutAttributesForElementsInRect來生成更新后的布局。
  • 在- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect方法中谬墙,獲取系統(tǒng)計算好的Attributes
// 獲取系統(tǒng)計算好的Attributes
NSArray * attributesToReturn = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
  • 在- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect方法中今布,遍歷系統(tǒng)的Attributes數(shù)組。
    // 遍歷
    for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
        /*
         
         typedef NS_ENUM(NSUInteger, UICollectionElementCategory) {
         UICollectionElementCategoryCell,
         UICollectionElementCategorySupplementaryView,
         UICollectionElementCategoryDecorationView
         };

         */
        // representedElementKind == nil 時 representedElementCategory 為 UICollectionElementCategoryCell 即此時的attributes為item
        if (nil == attributes.representedElementKind) {
            NSIndexPath* indexPath = attributes.indexPath;
            //對每個attributes的frame重新布局
            attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
        }
    }
  • 在- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath 方法中拭抬,對每個Attributes進行重新布局部默。
//定義cell的布局
 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
     // 獲取系統(tǒng)計算好的當(dāng)前的Attributes
    UICollectionViewLayoutAttributes * currentItemAttributes =
    [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
    
    //設(shè)置內(nèi)邊距
    UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout sectionInset];
    
    //如果是第一個item。則其frame.origin.x直接在內(nèi)邊距的左邊造虎。重置currentItemAttributes的frame 返回currentItemAttributes
    
    if (indexPath.item == 0) { // first item of section
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item of the section should always be left aligned
        currentItemAttributes.frame = frame;
        //返回currentItemAttributes
        return currentItemAttributes;
    }
    
    //如果不是第一個item傅蹂。則需要獲取前一個item的Attributes的frame屬性
    
    NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
    
    //前一個item與當(dāng)前的item的相鄰點
    CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width + kMaxCellSpacing;
    //當(dāng)前的frame
    CGRect currentFrame = currentItemAttributes.frame;
    //
    CGRect strecthedCurrentFrame = CGRectMake(0,
                                              currentFrame.origin.y,
                                              self.collectionView.frame.size.width,
                                              currentFrame.size.height);
    //判斷兩個結(jié)構(gòu)體是否有交錯.可以用CGRectIntersectsRect
    //如果兩個結(jié)構(gòu)體沒有交錯,則表明這個item與前一個item不在同一行上算凿。則其frame.origin.x直接在內(nèi)邊距的左邊份蝴。重置currentItemAttributes的frame 返回currentItemAttributes
    if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) {
        // if current item is the first item on the line
        // the approach here is to take the current frame, left align it to the edge of the view
        // then stretch it the width of the collection view, if it intersects with the previous frame then that means it
        // is on the same line, otherwise it is on it's own new line
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item on the line should always be left aligned
        currentItemAttributes.frame = frame;
        //返回currentItemAttributes
        return currentItemAttributes;
    }
    //如果如果兩個結(jié)構(gòu)體有交錯。將前一個item與當(dāng)前的item的相鄰點previousFrameRightPoint賦值給當(dāng)前的item的frame.origin.x
    CGRect frame = currentItemAttributes.frame;
    frame.origin.x = previousFrameRightPoint;
    currentItemAttributes.frame = frame;
    //返回currentItemAttributes
    return currentItemAttributes;
}
其中氓轰,具體的布局方案:

1.獲取系統(tǒng)計算好的當(dāng)前的Attributes

2.判斷當(dāng)前的Attributes是否是第一個搞乏。如果是直接返回。

3.如果當(dāng)前的Attributes不是第一個Attributes戒努。則需要獲取前一個item的Attributes请敦。將兩個Attributes的frame是否有交錯镐躲。使用CGRectIntersectsRect對比。

4.如果兩個結(jié)構(gòu)體沒有交錯侍筛,則表明當(dāng)前的Attributes與前一個Attributes不在同一行上萤皂。則設(shè)置當(dāng)前的Attributes的frame在下一行的左邊,并返回當(dāng)前的Attributes匣椰。

5.如果如果兩個結(jié)構(gòu)體有交錯裆熙。則表明當(dāng)前的Attributes與前一個Attributes在同一行上。則設(shè)置當(dāng)前的Attributes的frame在前一個Attributes的左邊禽笑,并返回當(dāng)前的Attributes入录。

出現(xiàn)警告:This is likely occurring because the flow layout subclass SkyLeftItemsCollectionViewFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them的解決方法
  • 如果是獲取系統(tǒng)計算好的Attributes時使用
    NSMutableArray *attributesToReturn = [[super layoutAttributesForElementsInRect:rect] mutableCopy]方法的話會有警告:
This is likely occurring because the flow layout subclass SkyLeftItemsCollectionViewFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them
  • 其解決方法就是在獲取系統(tǒng)計算好的Attributes時進行copyItems。
//定義屏幕展示的范圍和數(shù)量
 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    // 獲取系統(tǒng)計算好的Attributes
    NSArray * attributesToReturn = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];//copyItems將系統(tǒng)的Attributes進行拷貝
/*
     也可以這樣寫
     NSArray* arrayattributesToReturn = [super layoutAttributesForElementsInRect:rect];
     NSArray * attributesToReturn = [[NSArray alloc] initWithArray:arrayattributesToReturn copyItems:YES];
*/
  • 上述方法添加之后佳镜,運行還是有同樣的警告僚稿。我們還需對每個Attributes布局時進行copy。
//定義cell的布局
 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
     // 獲取系統(tǒng)計算好的當(dāng)前的Attributes
    UICollectionViewLayoutAttributes * currentItemAttributes =
    [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
}
以上便是蟀伸,CustomCollectionViewFlowLayout優(yōu)先居左的自定義布局的方法蚀同。

** Demo_github**

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啊掏,一起剝皮案震驚了整個濱河市蠢络,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌迟蜜,老刑警劉巖刹孔,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異娜睛,居然都是意外死亡芦疏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門微姊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酸茴,“玉大人,你說我怎么就攤上這事兢交⌒胶矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵配喳,是天一觀的道長酪穿。 經(jīng)常有香客問我,道長晴裹,這世上最難降的妖魔是什么被济? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮涧团,結(jié)果婚禮上只磷,老公的妹妹穿的比我還像新娘经磅。我一直安慰自己,他們只是感情好钮追,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布预厌。 她就那樣靜靜地躺著,像睡著了一般元媚。 火紅的嫁衣襯著肌膚如雪轧叽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天刊棕,我揣著相機與錄音炭晒,去河邊找鬼。 笑死甥角,一個胖子當(dāng)著我的面吹牛网严,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜈膨,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼屿笼,長吁一口氣:“原來是場噩夢啊……” “哼牺荠!你這毒婦竟也來了翁巍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤休雌,失蹤者是張志新(化名)和其女友劉穎灶壶,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杈曲,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡驰凛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了担扑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恰响。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涌献,靈堂內(nèi)的尸體忽然破棺而出胚宦,到底是詐尸還是另有隱情,我是刑警寧澤燕垃,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布枢劝,位于F島的核電站,受9級特大地震影響卜壕,放射性物質(zhì)發(fā)生泄漏您旁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一轴捎、第九天 我趴在偏房一處隱蔽的房頂上張望鹤盒。 院中可真熱鬧蚕脏,春花似錦、人聲如沸昨悼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽率触。三九已至终议,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間葱蝗,已是汗流浹背穴张。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留两曼,地道東北人皂甘。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像悼凑,于是被迫代替她去往敵國和親偿枕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355

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