- 一:監聽滾動停止的時刻
- 二、監聽reloadData重新整理列表完畢的時機
- 三、自由拖拽
- 四、瀑布流
- 五、浮動的Header
一:監聽滾動停止的時刻
場景1、手動拖拽螢幕停止
場景2、呼叫scrollToRow
停止
場景3、呼叫setContentOffset
停止
self.tableView.setContentOffset(CGPoint.init(x: 0, y: 0), animated: false)
print("執行這行程式碼的時候,已經滾動完畢")
複製程式碼
二、監聽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;
}
複製程式碼