在很多新聞客戶端中都有一個可以排序新聞類別的功能艘包。也用不少通過重寫UIScrollView來實現(xiàn)的统倒。這里主要說下通過UICollectionView怎么實現(xiàn),準確的說是通過CollectionViewFlowLayout來實現(xiàn)氛雪。
-
什么是UICollectionView
UICollectionView是一種新的數(shù)據(jù)展示方式房匆,簡單來說可以把他理解成多列的UITableView(請一定注意這是UICollectionView的最最簡單的形式)。如果你用過iBooks的話报亩,可能你還對書架布局有一定印象:一個虛擬書架上放著你下載和購買的各類圖書浴鸿,整齊排列
-
UICollectionView結(jié)構(gòu)
- Cells 用于展示內(nèi)容的主體,對于不同的cell可以指定不同尺寸和不同的內(nèi)容
- Supplementary Views 追加視圖 如果你對UITableView比較熟悉的話弦追,可以理解為每個Section的Header或者Footer岳链,用來標記每個section的view
- Decoration Views 裝飾視圖,這是每個section的背景
-
UICollectionViewLayout
UICollectionViewLayout是造就UICollectionView和UITableView最大不同的地方劲件。UICollectionViewLayout可以說是UICollectionView的大腦和中樞掸哑,它負責(zé)了將各個cell、Supplementary View和Decoration Views進行組織零远,為它們設(shè)定各自的屬性苗分,包括但不限于:
- 位置
- 尺寸
- 透明度
- 層級關(guān)系
- 形狀
- 等等等等…
- Layout決定了UICollectionView是如何顯示在界面上的。在展示之前牵辣,一般需要生成合適的UICollectionViewLayout子類對象摔癣,并將其賦予CollectionView的collectionViewLayout屬性。
長按可移動Cell的UICollectionViewLayout
1、通過UICollectionViewLayout給UICollectionView添加長按事件:主要通過監(jiān)聽layout是否被添加到collectionView中择浊。
-(void)addCollectionCreatedObserver{
[self addObserver:self forKeyPath:@"collectionView" options:NSKeyValueObservingOptionNew context:nil];
}
2戴卜、添加長按手勢
-(void)setupCollectionView{
self.longGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlerLongPressGesture:)];
self.longGestureRecognizer.delegate = self;
[self.collectionView addGestureRecognizer:self.longGestureRecognizer];
}
3、處理手勢
-(void)handlerLongPressGesture:(UILongPressGestureRecognizer *)gestureRecognizer{
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
[self startLongPressGesture:gestureRecognizer];
break;
case UIGestureRecognizerStateChanged:
[self moveLongPressGesture:gestureRecognizer];
break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:
[self endLongPressGesture:gestureRecognizer];
break;
default:
break;
}
}
4琢岩、處理手勢開始移動操作投剥,主要記錄移動的起點,對應(yīng)的Cell粘捎,并對當前Cell做快照薇缅,以便移動
self.startPoint = [gestureRecognizer locationInView:self.collectionView];
self.selectedMoveIndexPath = currentIndexPath;
UICollectionViewCell *collectionViewCell = [self.collectionView cellForItemAtIndexPath:self.selectedMoveIndexPath];
self.currentMoveView = [[UIView alloc] initWithFrame:collectionViewCell.frame];
collectionViewCell.highlighted = YES;
UIView *highlightedISnapshotView = [collectionViewCell MT_snapshotView];
highlightedISnapshotView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
highlightedISnapshotView.alpha = 1.0;
collectionViewCell.highlighted = NO;
UIView *snapshotView = [collectionViewCell MT_snapshotView];
snapshotView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
snapshotView.alpha = 0.0;
[self.currentMoveView addSubview:snapshotView];
[self.currentMoveView addSubview:highlightedISnapshotView];
[self.collectionView addSubview:self.currentMoveView];
self.currentViewCenter = self.currentMoveView.center;
5、處理手勢正在移動操作攒磨,主要記錄坐標變動泳桦,并移動快照視圖位置:
CGPoint currentPoint = [gestureRecognizer locationInView:self.collectionView];
self.currentPoint = CGPointMake(currentPoint.x - self.startPoint.x, currentPoint.y - self.startPoint.y);
CGPoint viewCenter = self.currentMoveView.center = MT_CGPointAdd(self.currentViewCenter, self.currentPoint);
刷新CollectionView
NSIndexPath *newIndexPath = [self.collectionView indexPathForItemAtPoint:self.currentMoveView.center];
NSIndexPath *preIndexPath = self.selectedMoveIndexPath;
if(newIndexPath == nil || [newIndexPath isEqual:preIndexPath]){
return;
}
self.selectedMoveIndexPath = newIndexPath;
if ([self.collectionView.dataSource respondsToSelector:@selector(collectionView:moveItemAtIndexPath:toIndexPath:)]) {
[self.collectionView.dataSource collectionView:self.collectionView moveItemAtIndexPath:preIndexPath toIndexPath:newIndexPath];
}
__weak typeof(self) weakSelf = self;
[self.collectionView performBatchUpdates:^{
[weakSelf.collectionView deleteItemsAtIndexPaths:@[preIndexPath]];
[weakSelf.collectionView insertItemsAtIndexPaths:@[newIndexPath]];
} completion:^(BOOL finished) {
}];
6、處理手勢結(jié)束移動娩缰。重置記錄:
NSIndexPath *currentIndexPath = self.selectedMoveIndexPath;
self.selectedMoveIndexPath = nil;
self.currentViewCenter = CGPointZero;
[self.currentMoveView removeFromSuperview];
self.currentMoveView = nil;
[self invalidateLayout];
7灸撰、重載layoutAttributesForElementsInRect:
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSArray *layoutAttributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *layoutAttributes in layoutAttributesForElementsInRect) {
if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell){
[self applyLayoutAttributes:layoutAttributes];
}
}
return layoutAttributesForElementsInRect;
}
8、重載layoutAttributesForItemAtIndexPath:
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
if(layoutAttributes.representedElementCategory == UICollectionElementCategoryCell){
[self applyLayoutAttributes:layoutAttributes];
}
return layoutAttributes;
}
9拼坎、隱藏當前選擇的Cell
-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
if([layoutAttributes.indexPath isEqual:self.selectedMoveIndexPath]){
layoutAttributes.hidden = YES;
}
}
移動Cell時可通知滾動UICollectionView
添加CADisplayLink浮毯,來主動觸發(fā)UICollectionView滾動。
1泰鸡、創(chuàng)建CADisplayLink
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
2债蓝、處理屏幕刷新事件:
CGSize frameSize = self.collectionView.bounds.size;
CGSize contentSize = self.collectionView.contentSize;
CGPoint contentOffset = self.collectionView.contentOffset;
UIEdgeInsets contentInset = self.collectionView.contentInset;
// Important to have an integer `distance` as the `contentOffset` property automatically gets rounded
// and it would diverge from the view's center resulting in a "cell is slipping away under finger"-bug.
CGFloat distance = rint(self.scrollingSpeed * displayLink.duration);
CGPoint translation = CGPointZero;
...
self.collectionView.contentOffset = MT_CGPointAdd(contentOffset, translation);
效果圖: