五個UICollectionView常用的知識點

場景2、呼叫 scrollToRow
停止

場景3、呼叫 setContentOffset
停止
self.tableView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: false) print("執行這行程式碼的時候,已經滾動完畢") 複製程式碼
二、監聽reloadData重新整理列表完畢的時機
詳情參考: 監聽reloadData重新整理列表完畢的時機
- 方法1、通過
layoutIfNeeded
方法,強制重繪並等待完成。 - 方法2、
reloadData
方法會在主執行緒執行,通過GCD
,使後續操作排隊在reloadData
後面執行。一次runloop
有兩個機會執行GCD dispatch main queue
中的任務,分別在休眠前和被喚醒後。設定listView
的layoutIfNeeded
為YES,在即將進入休眠時執行非同步任務,重繪一次介面。 - 方法3、自定義
UICollectionView
、UITableView
,layoutSubviews
之後當作reloadData
完成(複雜,但可以更好的理解方法一)
三、自由拖拽

一、iOS9之前思路:
- 第一步 :給
UICollectionviewCell
新增一個長按手勢UILongPressGestureRecognizer
,通過代理傳遞到UIViewController
中。 - 第二步 :開始長按時(
UIGestureRecognizerStateBegan
)對cell
進行截圖並且隱藏cell
。 - 第三步 :移動時(
UIGestureRecognizerStateChanged
)移動截圖,遍歷得到截圖移動到哪個cell
的位置。呼叫方法moveItemAtIndexPath:toIndexPath:
調換兩個cell
的位置,並且更新資料來源的順序。 - 第四步 :停止時(
UIGestureRecognizerStateEnded
)移除截圖,顯示cell
。
參考部落格:
LXReorderableCollectionViewFlowLayout
CollectionViewCellDragExchange
二、iOS9之後思路:
- 第一步:給
UICollectionviewCell
新增一個長按手勢UILongPressGestureRecognizer
,通過代理傳遞到UIViewController
中。 - 第二步:開始長按時(
UIGestureRecognizerStateBegan
)對cell進行截圖並且隱藏cell
。呼叫beginInteractiveMovementForItem
。 - 第三步:移動時(
UIGestureRecognizerStateChanged
)移動截圖。呼叫updateInteractiveMovementTargetPosition
。 - 第四步:停止時(
UIGestureRecognizerStateEnded
)移除截圖,顯示cell
。呼叫endInteractiveMovement
。
// Support for reordering @available(iOS 9.0, *) open func beginInteractiveMovementForItem(at indexPath: IndexPath) -> Bool // returns NO if reordering was prevented from beginning - otherwise YES @available(iOS 9.0, *) open func updateInteractiveMovementTargetPosition(_ targetPosition: CGPoint) @available(iOS 9.0, *) open func endInteractiveMovement() @available(iOS 9.0, *) open func cancelInteractiveMovement() 複製程式碼
參考部落格:
iOS UICollectionView高階用法(長按自由移動cell)-新
四、瀑布流

實現瀑布流的基本原理: 找到最短的列,然後把item放到最短的列下面
如下圖所示,由於第三列最短,所以第八個Item新增到第三列下面。

既然要找到 最短的列 ,則就需要用一個數據來儲存每一列的Y值,推薦 陣列 (相對來說效能比 字典 好)。 XRWaterfallLayout 使用 字典 來儲存,相比較 陣列 的下標直接獲取列的高度, 字典 多做了部分 雜湊操作
。做瀑布流,圖片的尺寸就可能是不固定的,圖片的尺寸可以伺服器提前返回,然後 cell
直接設定大小。
主要需要重寫三個系統方法:詳情參考實現: XRWaterfallLayout 1、 - (void)prepareLayout
2、 - (CGSize)collectionViewContentSize
3、 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
五、浮動的Header

一、在 iOS9
後
UICollectionView
的頭部檢視也能像 tableView
的 header
一樣出現懸浮掛住的效果。
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; //header flowLayout.sectionHeadersPinToVisibleBounds = YES; //footer flowLayout.sectionFootersPinToVisibleBounds = YES; 複製程式碼
二、在 iOS9.0
前
需要自定義 UICollectionViewFlowLayout
#import <UIKit/UIKit.h> @protocol LZSFloatHeaderFlowLayoutDelegate <NSObject> -(void)sectionDidFloat:(NSInteger)section; @end @interface LZSFloatHeaderFlowLayout : UICollectionViewFlowLayout @property (nonatomic, weak) id<LZSFloatHeaderFlowLayoutDelegate> mDelegate; @end 複製程式碼
#import "LZSFloatHeaderFlowLayout.h" @interface LZSFloatHeaderFlowLayout() @property (nonatomic, strong) NSMutableDictionary<NSNumber*, NSNumber*>* mSectionOffsetYDic; @end @implementation LZSFloatHeaderFlowLayout -(NSMutableDictionary<NSNumber *,NSNumber *> *)mSectionOffsetYDic { if ( !_mSectionOffsetYDic ) { _mSectionOffsetYDic = [NSMutableDictionary dictionary]; } return _mSectionOffsetYDic; } - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; for (NSUInteger idx=0; idx<[answer count]; idx++) { UICollectionViewLayoutAttributes *layoutAttributes = answer[idx]; if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { [missingSections addIndex:layoutAttributes.indexPath.section];// remember that we need to layout header for this section } if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { [answer removeObjectAtIndex:idx];// remove layout of header done by our super, we will do it right later idx--; } } // layout all headers needed for the rect using self code [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; [answer addObject:layoutAttributes]; }]; return answer; } - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { UICollectionView * const cv = self.collectionView; CGPoint const contentOffset = cv.contentOffset; CGPoint nextHeaderOrigin = CGPointMake(INFINITY, INFINITY); if (indexPath.section+1 < [cv numberOfSections]) { NSIndexPath* tIndexPath = [NSIndexPath indexPathForItem:0 inSection:indexPath.section+1] UICollectionViewLayoutAttributes *nextHeaderAttributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:tIndexPath]; nextHeaderOrigin = nextHeaderAttributes.frame.origin; } CGRect frame = attributes.frame; if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame)); } else { // UICollectionViewScrollDirectionHorizontal frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), nextHeaderOrigin.x - CGRectGetWidth(frame)); } attributes.zIndex = 1024; attributes.frame = frame; if ( self.mSectionOffsetYDic[@(indexPath.section)] && (self.mSectionOffsetYDic[@(indexPath.section)].integerValue != frame.origin.y) ) { if ( [self.mDelegate respondsToSelector:@selector(sectionDidFloat:)] ) { [self.mDelegate sectionDidFloat:indexPath.section]; } } self.mSectionOffsetYDic[@(indexPath.section)] = @(frame.origin.y); } return attributes; } - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; return attributes; } - (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; return attributes; } - (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { return YES; } 複製程式碼