導(dǎo)讀
拖拽排序是新聞?lì)惖腁pp可以說(shuō)是必有的交互設(shè)計(jì)和敬,如今日頭條,網(wǎng)易新聞等采桃。拖拽排序是一個(gè)交互體驗(yàn)非常好的設(shè)計(jì)懒熙,簡(jiǎn)單,方便普办。
- github地址:https://github.com/HelloYeah/DraggingSort 歡迎Star工扎,予人玫瑰,手有余香衔蹲。
今日頭條的拖拽排序界面
我實(shí)現(xiàn)的長(zhǎng)按拖拽排序效果
實(shí)現(xiàn)方案
1.給CollectionViewCell添加一個(gè)長(zhǎng)按手勢(shì)肢娘,通過(guò)代理把手勢(shì)傳遞到collectionView所在的控制器中。
- (void)awakeFromNib{
self.layer.cornerRadius = 3;
self.layer.masksToBounds = YES;
//給每個(gè)cell添加一個(gè)長(zhǎng)按手勢(shì)
UILongPressGestureRecognizer * longPress =[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
longPress.delegate = self;
[self addGestureRecognizer:longPress];
}
- (void)longPress:(UILongPressGestureRecognizer *)longPress{
if (self.delegate && [self.delegate respondsToSelector:@selector(longPress:)]) {
[self.delegate longPress:longPress];
}
}
2.開(kāi)始長(zhǎng)按時(shí)對(duì)cell進(jìn)行截圖舆驶,并隱藏cell橱健。
- (void)longPress:(UILongPressGestureRecognizer *)longPress{
//記錄上一次手勢(shì)的位置
static CGPoint startPoint;
//觸發(fā)長(zhǎng)按手勢(shì)的cell
MovingCell * cell = (MovingCell *)longPress.view;
//開(kāi)始長(zhǎng)按
if (longPress.state == UIGestureRecognizerStateBegan) {
[self shakeAllCell];
//獲取cell的截圖
_snapshotView = [cell snapshotViewAfterScreenUpdates:YES];
_snapshotView.center = cell.center;
[_collectionView addSubview:_snapshotView];
_indexPath= [_collectionView indexPathForCell:cell];
_originalCell = cell;
_originalCell.hidden = YES;
startPoint = [longPress locationInView:_collectionView];
}
3、在手勢(shì)移動(dòng)的時(shí)候沙廉,移動(dòng)截圖視圖拘荡,用遍歷的方法求出截圖移動(dòng)到哪個(gè)cell的位置,再調(diào)用系統(tǒng)的api交換這個(gè)cell和隱藏cell的位置撬陵,并且數(shù)據(jù)源中的數(shù)據(jù)也需要調(diào)整順序
//手勢(shì)移動(dòng)的時(shí)候
else if (longPress.state == UIGestureRecognizerStateChanged){
CGFloat tranX = [longPress locationOfTouch:0 inView:_collectionView].x - startPoint.x;
CGFloat tranY = [longPress locationOfTouch:0 inView:_collectionView].y - startPoint.y;
//設(shè)置截圖視圖位置
_snapshotView.center = CGPointApplyAffineTransform(_snapshotView.center, CGAffineTransformMakeTranslation(tranX, tranY));
startPoint = [longPress locationOfTouch:0 inView:_collectionView];
//計(jì)算截圖視圖和哪個(gè)cell相交
for (UICollectionViewCell *cell in [_collectionView visibleCells]) {
//跳過(guò)隱藏的cell
if ([_collectionView indexPathForCell:cell] == _indexPath) {
continue;
}
//計(jì)算中心距
CGFloat space = sqrtf(pow(_snapshotView.center.x - cell.center.x, 2) + powf(_snapshotView.center.y - cell.center.y, 2));
//如果相交一半且兩個(gè)視圖Y的絕對(duì)值小于高度的一半就移動(dòng)
if (space <= _snapshotView.bounds.size.width * 0.5 && (fabs(_snapshotView.center.y - cell.center.y) <= _snapshotView.bounds.size.height * 0.5)) {
_nextIndexPath = [_collectionView indexPathForCell:cell];
if (_nextIndexPath.item > _indexPath.item) {
for (NSUInteger i = _indexPath.item; i < _nextIndexPath.item ; i ++) {
[self.array exchangeObjectAtIndex:i withObjectAtIndex:i + 1];
}
}else{
for (NSUInteger i = _indexPath.item; i > _nextIndexPath.item ; i --) {
[self.array exchangeObjectAtIndex:i withObjectAtIndex:i - 1];
}
}
//移動(dòng)
[_collectionView moveItemAtIndexPath:_indexPath toIndexPath:_nextIndexPath];
//設(shè)置移動(dòng)后的起始indexPath
_indexPath = _nextIndexPath;
break;
}
}
4.手勢(shì)停止時(shí)珊皿,移除截圖的view网缝,顯示隱藏cell
//手勢(shì)停止時(shí)
}else if(longPress.state == UIGestureRecognizerStateEnded){
[self stopShake];
[_snapshotView removeFromSuperview];
_originalCell.hidden = NO;
}
其他
代碼還可以進(jìn)一步封裝,寫(xiě)一個(gè)數(shù)據(jù)管理類(lèi)dataTool蟋定,dataTool作為collectionView的數(shù)據(jù)源粉臊,所有的數(shù)據(jù)源方法都寫(xiě)到dataTool類(lèi)中。手勢(shì)的代理方法也在里面實(shí)現(xiàn)驶兜,這樣控制器會(huì)簡(jiǎn)潔很多维费,控制器就不需要關(guān)注拖拽排序的具體邏輯了。大家有空可以自己寫(xiě)寫(xiě)看促王,也許你們有更好的處理方案犀盟,可以評(píng)論交流一下。
github地址:https://github.com/HelloYeah/DraggingSort
提示
如果你們是從iOS9開(kāi)始適配的話(huà)蝇狼,那么可以用系統(tǒng)的Api阅畴,非常簡(jiǎn)單好用
//是否允許拖拽
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
/**
* 從sourceIndexPath 拖拽至destinationIndexPath,在這個(gè)代理方法里修改數(shù)據(jù)源即可迅耘。
* exchangeObjectAtIndex:sourceIndexPath.item withObjectAtIndex: destinationIndexPath.item
*/
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0);