UICollectionView左對齊流水佈局、右對齊流水佈局

滴水微瀾發表於2017-05-25

 

    在平時使用的app中會經常碰到一些規格選擇,篩選,標籤等頁面,這些頁面的佈局展示通常是左對齊流水佈局。
實現類似這樣的左對齊流水佈局有多種方式,如果選項少的話可以直接用UIButton實現。現在我們有一種比較簡單的方式可以實現這個目的。
就是對UICollectionView稍加改動,就能輕鬆實現。
下面介紹一下具體實現的方法。

    通過 ZFFlowLayout類可以建立一個預設距離的佈局例項,也可以建立一個自定義距離的佈局例項。

#import <UIKit/UICollectionViewFlowLayout.h>
#import "ZFFlowLayoutMacro.h"
#import "ZFFlowLayoutProtocol.h"

//流水佈局型別
typedef enum : NSUInteger {
    FlowLayoutType_leftAlign,
    FlowLayoutType_rightAlign,
} FlowLayoutType;

@interface ZFFlowLayout : NSObject

/*!
 *  @author zhoufei
 *
 *  @brief 根據傳入不同的流失佈局型別獲取不同的佈局例項
 *  @param flowLayoutType 流水佈局型別
 *  @return 佈局例項
 */
+ (UICollectionViewFlowLayout *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType;


/*!
 *  @author zhoufei
 *
 *  @brief 自定義佈局例項:根據傳入不同的流失佈局型別,item距離四周距離,section距離四周距離 自定義佈局例項
 *  @param flowLayoutType 流水佈局型別
 *  @param itemEdgeInsets 第一個item距離四周的距離
 *  @param sectionEdgeInsets section距離四周的距離
 *  @return 佈局例項
 */
+ (UICollectionViewFlowLayout<ZFFlowLayoutProtocol> *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType
                                                                    ItemEdgeInsets:(FlowLayoutItemEdgeInsets)itemEdgeInsets
                                                                 sectionEdgeInsets:(FlowLayoutSectionEdgeInsets)sectionEdgeInsets;

呼叫如下方法可以根據想要建立的佈局型別,生成一個佈局實現。

+ (UICollectionViewFlowLayout *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType;

呼叫如下方法可以根據想要建立的佈局型別和第一個item距離四周的距離與section距離四周的距離,生成一個自定義的佈局實現。

+ (UICollectionViewFlowLayout<ZFFlowLayoutProtocol> *)flowLayoutWithFlowLayoutType:(FlowLayoutType)flowLayoutType
                                                                    ItemEdgeInsets:(FlowLayoutItemEdgeInsets)itemEdgeInsets
                                                                 sectionEdgeInsets:(FlowLayoutSectionEdgeInsets)sectionEdgeInsets;

在第二個方法中使用到了自定義的列舉和結構體,它們的具體實現如下:
#ifndef ZFFlowLayoutMacro_h
#define ZFFlowLayoutMacro_h

/*!** 
 左對齊佈局時:左上角第一個item 距離四周的距離
 右對齊佈局時:右上角第一個item 距離四周的距離
  ***/
typedef struct FlowLayoutItemEdgeInsets {
    CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} FlowLayoutItemEdgeInsets;

/*!** item所屬的組section 距離四周的距離 ***/
typedef struct FlowLayoutSectionEdgeInsets {
    CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
} FlowLayoutSectionEdgeInsets;

#endif /* ZFFlowLayoutMacro_h */

結構體中值得具體含義已經在註釋中寫出,這裡就不在講了。

下面講一下最核心的類 LeftAlignedFlowLayout類

因為這個類程式碼略有點長,這裡就這貼出主要的邏輯程式碼:

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray* attributes = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    
    NSMutableArray * subArray = [LayoutAttributeTools groupTheSameLineItems:attributes];

    [self leftAlign_updateItemAttributeInSigleLine:subArray];
 
    return attributes;
}

/*!
 *  @author zhoufei
 *
 *  @brief 更新每個元素的位置
 *  @param groupArray 歸併後的結果陣列
 */
- (void)leftAlign_updateItemAttributeInSigleLine:(NSMutableArray * )groupArray{
    
    NSMutableArray * modelArray = [NSMutableArray array];
    
    for (NSArray * array  in groupArray) {
        
        NSInteger count = array.count;
        
        if (!count) {
            continue;
        }
        
        for (int i = 0; i<count; i++) {
            UICollectionViewLayoutAttributes *attrOne = array[i];
            [modelArray addObject:[Attribute AttributeWithIndex:i width:attrOne.size.width]];
            
        }
        
        CGFloat leftWith = 0;
        
        for (int i=0; i<count; i++) {
            
            UICollectionViewLayoutAttributes *attr = [array objectAtIndex:i];
            
            NSPredicate *predice =[NSPredicate predicateWithFormat:@"index < %d",i];
            NSArray * resultArray = [modelArray filteredArrayUsingPredicate:predice];
            NSNumber * number = [resultArray valueForKeyPath:@"@sum.width"];
            
            leftWith = self.leftMargin+self.itemMargin*i+number.doubleValue;
            
            CGRect frame = attr.frame;
            frame.origin.x = leftWith;
            attr.frame = frame;
            
        }
        [modelArray removeAllObjects];
    }
    
}

上面這個方法
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
是父類 UICollectionViewFlowLayout的代理方法,在這個方法中可以拿到當前螢幕中顯示的所有 UICollectionViewCell的佈局實現,我們對
UICollectionViewCell的佈局修改也就是在這個方法中。
首先通過方法 [LayoutAttributeTools groupTheSameLineItems:attributes];對螢幕中顯示的每一行 UICollectionViewCell 進行分組。這樣分組之後邏輯比較清晰。只需要設定每一行UICollectionViewCell的新佈局例項,剩餘的也都是有每一行組成的。直接來個迴圈就搞定了。
方法 [self leftAlign_updateItemAttributeInSigleLine:subArray];就是對分組後的UICollectionViewCell進行逐行更新佈局例項物件的值。具體實現已經在程式碼中給出了。

Demo地址:https://github.com/zhfei/ZFFlowLayout

  歡迎star。

  如果發現不對的地方歡迎批評和指正。

相關文章