iOS UICollectionView自定義流水布局(一)

UICollectionViewFlowLayout

1.直接設(shè)置FlowLayout對(duì)象

創(chuàng)建UICollectionViewLayout對(duì)象,通過(guò)設(shè)置UICollectionViewLayout對(duì)象屬性的值可以設(shè)置item的基本布局,包括大小,間距,內(nèi)邊距、滾動(dòng)方向等

     UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.minimumLineSpacing = 10;
    layout.minimumInteritemSpacing = 10;
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);

2.自定義布局

當(dāng)系統(tǒng)的布局無(wú)論怎么設(shè)置都無(wú)法滿足需求的時(shí)候,我們就需要自定義布局,重寫其計(jì)算布局的方法。


圖片.png

圖片.png

上圖是系統(tǒng)默認(rèn)布局,下圖是自定義布局,可以注意到系統(tǒng)布局是將item兩端對(duì)齊,間距根據(jù)剩余的寬度自己縮放,UICollectionViewFlowLayout的minimumInteritemSpacing屬性設(shè)置的是最小間距,但如果要設(shè)置間距相等,系統(tǒng)布局就實(shí)現(xiàn)不了了。

3.下面通過(guò)兩種方案實(shí)現(xiàn)

兩種方案都是通過(guò)繼承UICollectionViewFlowLayout,這兩種方案本質(zhì)上都是通過(guò)修改對(duì)應(yīng)位置的item來(lái)實(shí)現(xiàn)的

第一種方案:重寫layoutAttributesForElementsInRect:方法返回指定區(qū)域cellt的布局, 此方法會(huì)多次調(diào)用,為了更好的性能,在這個(gè)方法當(dāng)中,我們使用的UICollectionViewLayoutAttributes最好是在prepareLayout已經(jīng)布局好的信息。

1.//獲取指定位置的cell的布局

- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *originAttributes = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray *updatedAttributes = [NSMutableArray arrayWithArray:originAttributes];
    for (UICollectionViewLayoutAttributes *attributes in originAttributes) {
        if (!attributes.representedElementKind) {
            NSUInteger index = [updatedAttributes indexOfObject:attributes];
            updatedAttributes[index] = [self layoutAttributesForItemAtIndexPath:attributes.indexPath];
        }
    }
    return updatedAttributes ;
}

2.重寫layoutAttributesForItemAtIndexPath:方法返回指定indexPath的布局

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *currentAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    UIEdgeInsets sectionInset = self.sectionInset;
    
    BOOL isFirstItemInSection = indexPath.item == 0;
    CGFloat layoutWidth = CGRectGetWidth(self.collectionView.frame) - sectionInset.left - sectionInset.right;
    if (isFirstItemInSection) {
        [currentAttributes leftAlignFrameWithSectionInset:sectionInset];
        return  currentAttributes;
    }
    
    NSIndexPath *previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item - 1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
    CGRect currentFrame = currentAttributes.frame;
    CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width;
    CGRect strecthedCurrentFrame = CGRectMake(sectionInset.left, currentFrame.origin.y, layoutWidth, currentFrame.size.height);
    //previousFrame和strecthedCurrentFrame不相交證明不在一行
    BOOL isFirstItemInRow = !CGRectIntersectsRect(previousFrame, strecthedCurrentFrame);
    if (isFirstItemInRow) {
        [currentAttributes leftAlignFrameWithSectionInset:sectionInset];
        return  currentAttributes;
    }
    
    CGRect frame = currentAttributes.frame;
    frame.origin.x = previousFrameRightPoint + self.minimumInteritemSpacing;
    currentAttributes.frame = frame;
    return  currentAttributes;
}
第二種方案

1.重寫prepareLayout,每次更新布局的時(shí)候collectionView都回先調(diào)用這個(gè)方法,為將要開始的更新做準(zhǔn)備,此時(shí)會(huì)將計(jì)算好的布局存儲(chǔ)起來(lái)

//重寫其布局方法
- (void)prepareLayout {
    [super prepareLayout];
    
    NSMutableArray *layoutInfoArr = [NSMutableArray array];
    //獲取布局信息
    NSInteger numberOfSections = [self.collectionView numberOfSections];
    for (NSInteger section = 0; section < numberOfSections; section++) {
        NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section];
        NSMutableArray *subArr = [NSMutableArray arrayWithCapacity:numberOfItems];
        for (NSInteger item = 0; item < numberOfItems; item++) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
            UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
            [subArr addObject:attributes];
        }
        [layoutInfoArr addObject:[subArr copy]];
    }
    
    self.attributesArray = [layoutInfoArr copy];
}

2.重寫layoutAttributesForElementsInRect:方法返回指定區(qū)域的布局

//指出了與指定區(qū)域有交接的UICollectionViewLayoutAttributes對(duì)象放到一個(gè)數(shù)組中,然后返回
- (NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *layoutAttributesArr = [NSMutableArray array];
    
    [self.attributesArray enumerateObjectsUsingBlock:^(NSArray * _Nonnull array, NSUInteger idx, BOOL * _Nonnull stop) {
        [array enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes  * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (CGRectIntersectsRect(obj.frame, rect)) { //如果item在rect內(nèi)
                [layoutAttributesArr addObject:obj];
            }
        }];
    }];
    return layoutAttributesArr;
}

3.重寫并調(diào)用layoutAttributesForItemAtIndexPath方法計(jì)算布局

//計(jì)算每一個(gè)item的布局
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    //indexPath對(duì)應(yīng)的系統(tǒng)為我們計(jì)算的布局
    UICollectionViewLayoutAttributes *oldAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    
    //創(chuàng)建一個(gè)我們期望的布局
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    CGFloat itemX = self.sectionInset.left;//默認(rèn)X值
    CGFloat itemY = oldAttributes.frame.origin.y;//Y值直接用系統(tǒng)算的
    CGSize itemSize = oldAttributes.size; //大小代理直接返回的
    
    //同一行
    BOOL line = oldAttributes.frame.origin.y == self.lastFrame.origin.y;
    
    //無(wú)需換行&&indexpath.row != 0調(diào)整X值,(indexPath.row = 0)時(shí)self.lastFrame還未賦值
    if (oldAttributes.frame.origin.x != itemX && indexPath.row != 0 && line) {
        itemX = self.lastFrame.origin.x + self.lastFrame.size.width + self.minimumLineSpacing;
    }
    
    attributes.frame = CGRectMake(itemX, itemY, itemSize.width, itemSize.height);
    
    //更新上一個(gè)item的位置
    self.lastFrame = CGRectMake(itemX, itemY, itemSize.width, itemSize.height);
    
    return  attributes;
}

4.如果需要自定義分區(qū)頭部和尾部可以重寫下面兩個(gè)方法,并在prepareLayout里面做相應(yīng)的處理

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;

demo:https://github.com/SunshineLily/CollectionViewFlowLayout
參考:https://blog.csdn.net/lg767201403/article/details/90518273
參考:http://www.itdecent.cn/p/5ee9333644ed

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2021.12.15今日體驗(yàn): 無(wú)論工作還是生活中,我們都要保持一個(gè)積極的心態(tài),做好迎接各種挑戰(zhàn)的準(zhǔn)備,遇到逆境也...
    京心達(dá)_周莎閱讀 140評(píng)論 0 0
  • 15.活在未來(lái)的最樸素的方法是什么? 用正確的方法去做正確的事情。那么要判定方法、判斷事情是否正確?就需要我們的“...
    一杯苦茶閱讀 173評(píng)論 0 0
  • 中原焦點(diǎn)團(tuán)隊(duì)網(wǎng)絡(luò)中級(jí)第28期堅(jiān)持分享第211天+約練73次(2021.12.15星期三) 讀書: 人的成長(zhǎng)不能僅靠...
    淘小寶_38f1閱讀 160評(píng)論 0 0
  • 在充滿迷霧的夜晚,往盒子裝進(jìn)兩個(gè)秘密,一個(gè)關(guān)于月亮,一個(gè)關(guān)于太陽(yáng),然后把盒子放入星海里。
    豌豆00閱讀 159評(píng)論 0 0
  • 最近的這次流感,真的很嚴(yán)重,好多孩子請(qǐng)假。兒子因?yàn)榭人詴簳r(shí)還不能去學(xué)校,白天的時(shí)候也只能在樓下逛一逛。我想如此的單...
    光陰里那些手繪的花朵閱讀 108評(píng)論 0 0

友情鏈接更多精彩內(nèi)容