你好,歡迎來到IOS教程網

 Ios教程網 >> IOS使用技巧 >> IOS技巧綜合 >> 干貨之UICollectionViewFlowLayout自定義排序和拖拽手勢

干貨之UICollectionViewFlowLayout自定義排序和拖拽手勢

編輯:IOS技巧綜合
[摘要]本文是對干貨之UICollectionViewFlowLayout自定義排序和拖拽手勢的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。

使用UICollectionView,需要使用UICollectionViewLayout控制UICollectionViewCell布局,雖然UICollectionViewLayout提供了高度自定義空間,但是對於日常使用顯得太繁瑣,於是常見使用UICollectionViewFlowLayout。除了提供UITableView類似的協議方法,後者還提供了協議UICollectionViewDelegateFlowLayout <UICollectionViewDelegate>,定義了返回cell尺寸、間距,section的insets,header、footer尺寸等方法。從iOS9開始,UICollectionViewDelegate增加了cell的move相關的協議方法。但是不同尺寸的cell排序效果仍需要自行實現。

為了支持iOS7以上,實現常見不同尺寸cell自定義排序,故實現了一個UICollectionViewFlowLayout的子類,並提供了一個UICollectionViewLayout分類支持自定義拖拽手勢。

查閱了一些資料,實現不同尺寸cell自定義排序的文章已經不少,常見都是描述了實現不具有header和footer的獨個section的items排序,但是基本都未完整支持UICollectionViewFlowLayout的功能。

我封裝了一個子類,完全支持UICollectionViewFlowLayout的功能,並額外提供了設置section背景顏色和所有section的items規則排序的擴展功能,鑒於代碼數量,在此記錄一下關鍵的實現過程。

ALWCollectionViewFlowLayout

1.設置section背景顏色

如果設置了UICollectionView的背景色,但是需要不同section顯示不同顏色,就只需要自行在子類實現了。

UICollectionView的內容view可以分為三類:SupplementaryView(header和footer),Cell(item),DecorationView(多用在cell下層)

設置section的背景色,可以控制decorationView的背景色來實現。

UICollectionViewLayout提供了一個如下方法

- (void)registerClass:(nullable Class)viewClass forDecorationViewOfKind:(NSString *)elementKind;

可以在子類init方法中,注冊一個UICollectionReusableView的子類ALWCollectionReusableView,作為decorationView。在ALWCollectionReusableView中,重載方法- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes;這裡的layoutAttributes對象,具有眾多屬性,唯獨沒有一個UIColor。理所當然,再實現一個UICollectionViewLayoutAttributes的子類,增加一個UIColor屬性名為backgroundColor,可以設置默認值。然後在applyLayoutAttributes方法中,如下實現:

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
    [super applyLayoutAttributes:layoutAttributes];
    
    if ([layoutAttributes isMemberOfClass:[ALWCollectionViewLayoutAttributes class]]) {
        self.backgroundColor = ((ALWCollectionViewLayoutAttributes *)layoutAttributes).backgroundColor;
    }
}

以上,只是注冊和設置了decorationView的背景色,但是還未設置裝飾view的顯示frame和在合適的時機使其生效。

這個時機,就是UICollectionViewFlowLayout的如下兩個方法

- (void)prepareLayout;

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;

前者是設置屬性的時機,後者是使其生效的時機。

A.為了便於動態設置每個section的背景色,提供了一個協議方法

- (UIColor *)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout backgroundColorForSectionAtIndex:(NSInteger)section;

B.計算decorationView尺寸時候,可以根據每個section的首尾item的frame和sectionInset來確定;當同時有Header和footer時候,也可以根據二者來確定。但是需要注意,如果部分header或者footer未實現,在獲取布局屬性對象時候會為nil

C.在UICollectionViewFlowLayout子類中可使用如下實例方法獲取布局屬性對象

- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;

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

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

但是在UICollectionViewLayout中將會返回nil,可以使用UICollectionViewLayoutAttributes的類方法得到實例

+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath;

+ (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath;

+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;

2.自定義不同尺寸的item的排序

UICollectionViewFlowLayout已經很好的展示了等尺寸的item排列,但是不同尺寸的item排列則顯得不規則,如果希望在使用父類的完整功能基礎上,每排item按照固定間距排列,可以按照如下記錄實現。

為了間距規則,所以我的實現前提是固定橫向豎向其中一個方向每排的item數量、邊長都相等。如下圖顯示了填充類型的排序演示效果:

大致記錄一下實現過程:

A.重載方法- (void)prepareLayout,在方法中得到item、header、footer的布局屬性數組(NSMutableArray<UICollectionViewLayoutAttributes*>)

a.以縱向滑動為例,循環累加每個section的header、item、footer高度

b.根據每行item數量決定高度占用數組元素數量,記錄每列item垂直方向當前占用的內容高度

c.以填充排序為例,每個item的y方向偏移量由高度占用數組最小元素決定;x方向由sectionInset、item固定的寬度、橫向間距、列的索引共同決定;尺寸方面只需要獲取itemSize協議方法返回尺寸中的高度

B.重載方法- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;返回items、header、footer、decorationView的屬性數組

C.重載方法- (CGSize)collectionViewContentSize,返回A過程中記錄的占用的最大內容尺寸

D.特別注意,不能在super方法返回的數組基礎上,再添加Header、footer的布局屬性。這樣可能會出現如下錯誤

layout attributes for supplementary item at index path (<NSIndexPath>) 
changed from <UICollectionViewLayoutAttributes> 
to <UICollectionViewLayoutAttributes>
without invalidating the layout

就是由於布局屬性數組中,存在相同indexPath的布局屬性對象。

UICollectionViewLayout (DragGesture)

提供支持iOS7以上的拖拽手勢,適用於所有子類。iOS9以後,UICollectionView提供了move相關方法,只需要添加手勢觸發調用相關方向即可。我實現的分類提供了類似的方法,為了不混淆使用,還提供了啟用屬性,默認關閉。

實現過程:

1.交換init方法,在其中增加collectionView屬性的KVO,因為UICollectionView的實例化一般在UICollectionViewLayout之後

2.在UICollectionView實例化後,根據啟用屬性,添加長按手勢和拖動手勢到UICollectionView上

3.如果選中了某個item,開始持續關注拖動位置,進入另一個item後,交換二者,實現動畫和數據交換。主要涉及UICollectionView的方法

- (void)performBatchUpdates:(void (^ __nullable)(void))updates completion:(void (^ __nullable)(BOOL finished))completion;

4.增加CADisplayLink對象,根據拖動方向和item所處位置,以屏幕刷新頻率和預定移動速度,自動移動UICollectionView內容偏移量

效果圖:

備注:目前在自定義排序後,拖拽手勢效果仍存在短暫閃爍的問題,會持續修復。

猜測問題在重載返回屬性數組的方法中,因為使用UICollectionViewFlowLayout默認排序時候,拖拽效果沒有問題。

如果有朋友找到問題所在,請留言,非常感謝。

該類庫已經在Base項目中更新:https://github.com/ALongWay/base.git

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved