来源:wazrx

链接:http://www.jianshu.com/p/8f0153ce17f9

写在前面

这段时间都在忙新项目的事儿,没有时间倒腾,这两天闲下来,想着一直没有细细的研究CollectionView,一般最多用来做点循环滚动,所以花时间深入学习了一些东西,这次实现了CollectionView的拖动重排的效果,先请看图:(吐槽:不知道为啥从xcode7开始,模拟器变得很卡很卡,所以截图的效果不好,大家可以在真机上测试,效果还是非常不错的)

2月27日更新:

修复了拖拽滚动时抖动的一个bug,新增编辑模式,进入编辑模式后不用长按触发手势,且在开启抖动的情况下会自动进入抖动模式,如图:

配合瀑布流(我直接使用了上个项目的瀑布流模块做了集成实验)

我将整个控件进行了封装,名字是XWDragCellCollectionView使用起来非常方便,github地址:https://github.com/wazrx/XWDragCellCollectionView;使用也非常简单,只需3步,步骤如下:

1、继承于XWDragCellCollectionView;

2、实现必须实现的DataSouce代理方法:(在该方法中返回整个CollectionView的数据数组用于重排)

- (NSArray *)dataSourceArrayOfCollectionView:(XWDragCellCollectionView *)collectionView;

3、实现必须实现的一个Delegate代理方法:(在该方法中将重拍好的新数据源设为当前数据源)(例如 :_data = newDataArray)

- (void)dragCellCollectionView:(XWDragCellCollectionView *)collectionView newDataArrayAfterMove:(NSArray *)newDataArray;

详细的使用可以查看代码中的demo,支持设置长按事件,是否开启边缘滑动,抖动、以及设置抖动等级,这些在h文件里面都有详细说明,有需要的可以尝试一下,并多多提意见,作为新手,肯定还有很多不足的地方;

原理

在刚刚考虑这个效果的时候,我仔细分析了一下效果,我首先想到的就是利用截图大法,将手指要移动的cell截个图来进行移动,并隐藏该cell,然后在合适的时候交换cell的位置,造成是拖拽cell被拖拽到新位置的效果,我将主要实现的步骤分为如下步骤:

1、给CollectionView添加一个长按手势,用于效果驱动

- (void)xwp_addGesture{

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(xwp_longPressed:)];

_longPressGesture = longPress;

//设置长按时间

longPress.minimumPressDuration = _minimumPressDuration;

[self addGestureRecognizer:longPress];

}

2、在手势开始的时候,得到手指所在的cell,并截图,并将原有cell隐藏

- (void)xwp_gestureBegan:(UILongPressGestureRecognizer *)longPressGesture{

//获取手指所在的cell

_originalIndexPath = [self indexPathForItemAtPoint:[longPressGesture locationOfTouch:0 inView:longPressGesture.view]];

UICollectionViewCell *cell = [self cellForItemAtIndexPath:_originalIndexPath];

//截图大法,得到cell的截图视图

UIView *tempMoveCell = [cell snapshotViewAfterScreenUpdates:NO];

_tempMoveCell = tempMoveCell;

_tempMoveCell.frame = cell.frame;

[self addSubview:_tempMoveCell];

//隐藏cell

cell.hidden = YES;

//记录当前手指位置

_lastPoint = [longPressGesture locationOfTouch:0 inView:longPressGesture.view];

}

3、在手势移动的时候,计算出手势移动的距离,并移动截图视图,当截图视图于某一个cell(可见cell)相交到一定程度的时候,我就让调用系统的api交换这个cell和隐藏cell的位置,形成动画,同时更新数据源(更新数据源是最重要的操作!)

- (void)xwp_gestureChange:(UILongPressGestureRecognizer *)longPressGesture{

//计算移动距离

CGFloat tranX = [longPressGesture locationOfTouch:0 inView:longPressGesture.view].x - _lastPoint.x;

CGFloat tranY = [longPressGesture locationOfTouch:0 inView:longPressGesture.view].y - _lastPoint.y;

//设置截图视图位置

_tempMoveCell.center = CGPointApplyAffineTransform(_tempMoveCell.center, CGAffineTransformMakeTranslation(tranX, tranY));

_lastPoint = [longPressGesture locationOfTouch:0 inView:longPressGesture.view];

//计算截图视图和哪个cell相交

for (UICollectionViewCell *cell in [self visibleCells]) {

//剔除隐藏的cell

if ([self indexPathForCell:cell] == _originalIndexPath) {

continue;

}

//计算中心距

CGFloat space = sqrtf(pow(_tempMoveCell.center.x - cell.center.x, 2) + powf(_tempMoveCell.center.y - cell.center.y, 2));

//如果相交一半就移动

if (space

/**

*  更新数据源

*/

- (void)xwp_updateDataSource{

NSMutableArray *temp = @[].mutableCopy;

//通过代理获取数据源,该代理方法必须实现

if ([self.dataSource respondsToSelector:@selector(dataSourceArrayOfCollectionView:)]) {

[temp addObjectsFromArray:[self.dataSource dataSourceArrayOfCollectionView:self]];

}

//判断数据源是单个数组还是数组套数组的多section形式,YES表示数组套数组

BOOL dataTypeCheck = ([self numberOfSections] != 1 || ([self numberOfSections] == 1 && [temp[0] isKindOfClass:[NSArray class]]));

//先将数据源的数组都变为可变数据方便操作

if (dataTypeCheck) {

for (int i = 0; i  _originalIndexPath.item) {

for (NSUInteger i = _originalIndexPath.item; i  _moveIndexPath.item ; i --) {

[orignalSection exchangeObjectAtIndex:i withObjectAtIndex:i - 1];

}

}

}else{

//在不同section之间移动的情况(原理是删除原位置所在section的cell并插入到新位置所在的section中)

NSMutableArray *orignalSection = temp[_originalIndexPath.section];

NSMutableArray *currentSection = temp[_moveIndexPath.section];

[currentSection insertObject:orignalSection[_originalIndexPath.item] atIndex:_moveIndexPath.item];

[orignalSection removeObject:orignalSection[_originalIndexPath.item]];

}

//将重排好的数据传递给外部,在外部设置新的数据源,该代理方法必须实现

if ([self.delegate respondsToSelector:@selector(dragCellCollectionView:newDataArrayAfterMove:)]) {

[self.delegate dragCellCollectionView:self newDataArrayAfterMove:temp.copy];

}

}

4、手势结束的时候将截图视图动画移动到隐藏cell所在位置,并显示隐藏cell并移除截图视图;

- (void)xwp_gestureEndOrCancle:(UILongPressGestureRecognizer *)longPressGesture{

UICollectionViewCell *cell = [self cellForItemAtIndexPath:_originalIndexPath];

//结束动画过程中停止交互,防止出问题

self.userInteractionEnabled = NO;

//给截图视图一个动画移动到隐藏cell的新位置

[UIView animateWithDuration:0.25 animations:^{

_tempMoveCell.center = cell.center;

} completion:^(BOOL finished) {

//移除截图视图、显示隐藏cell并开启交互

[_tempMoveCell removeFromSuperview];

cell.hidden = NO;

self.userInteractionEnabled = YES;

}];

}

关键效果的代码就是上面这些了,还有写细节的东西请大家自行查看源代码

写在最后

从iOS9开始,系统已经提供了重排的API,不用我们这么辛苦的自己写,不过想要只适配iOS9,还有一段时间,不过大家可以尝试去实现以下这几个API:

// Support for reordering

- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0); // returns NO if reordering was prevented from beginning - otherwise YES

- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition NS_AVAILABLE_IOS(9_0);

- (void)endInteractiveMovement NS_AVAILABLE_IOS(9_0);

- (void)cancelInteractiveMovement NS_AVAILABLE_IOS(9_0);

接下来,还准备研究一下CollectionView的转场和自定义布局,已经写了一些自定义布局效果了,总结好了再贴出来,CollectionView实在是一枚非常强大的控件,大家都应该去深入的研究一下,说不定会产生许多奇妙的想法!加油咯!最后复习一下github地址:https://github.com/wazrx/XWDragCellCollectionView,如果觉得有帮助,请给与一颗star鼓励一下,谢谢!

可拖拽重排的CollectionView的更多相关文章

  1. iOS之可拖拽重排的CollectionView

    修复了拖拽滚动时抖动的一个bug,新增编辑模式,进入编辑模式后不用长按触发手势,且在开启抖动的情况下会自动进入抖动模式,如图: test.gif 图1:垂直滚动 drag1.gif 图2:水平滚动 d ...

  2. iOS-UICollectionView快速构造/拖拽重排/轮播实现

    代码地址如下:http://www.demodashi.com/demo/11366.html 目录 UICollectionView的定义 UICollectionView快速构建GridView网 ...

  3. iOS开发之--storyboary下,拖拽一个tableview/collectionView/view 等,顶端下沉64个像素的处理方法

    大家可能会发现,在sb或者xib里面拖拽一个tableview/collectionview/view的,顶端会自动下沉64个像素,也就是说,运行在模拟器上去,自导航下面又自动下沉了64个像素, 那是 ...

  4. Swift2.0下UICollectionViews拖拽效果的实现

    文/过客又见过客(简书作者)原文链接:http://www.jianshu.com/p/569c65b12c8b著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 原文UICollecti ...

  5. 【UWP】拖拽列表项的排序功能实现

    在一些允许用户自定义栏目顺序的app(如:凤凰新闻.网易云音乐等),我们可以方便地拖拽列表项来完成列表的重新排序,进而完成对栏目顺序的重排.这个功能很人性化,而实现起来其实很简单(甚至都不用写什么后台 ...

  6. 干货之UICollectionViewFlowLayout自定义排序和拖拽手势

    使用UICollectionView,需要使用UICollectionViewLayout控制UICollectionViewCell布局,虽然UICollectionViewLayout提供了高度自 ...

  7. Dev Grid拖拽移动行

    效果图 源码下载 拖拽时带行截图效果实现代码 /// <summary> /// 拖拽帮助类 /// </summary> public static class DragHe ...

  8. 通过layout实现可拖拽自动排序的UICollectionView

    文/CenturyGuo(简书作者)原文链接:http://www.jianshu.com/p/8d1bf1838882著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. Translat ...

  9. 【Swift 4.0】iOS 11 UICollectionView 长按拖拽删除崩溃的问题

    正文 功能 用 UICollectionView 实现两个 cell 之间的位置交互或者拖拽某个位置删除 问题 iOS 11 以上拖拽删除会崩溃,在 iOS 9.10 都没有问题 错误 017-10- ...

随机推荐

  1. !!对python列表学习整理列表及数组详细介绍

    1.Python的数组分三种类型:(详细见 http://blog.sina.com.cn/s/blog_6b783cbd0100q2ba.html) (1) list 普通的链表,初始化后可以通过特 ...

  2. hdu4671Backup Plan

    http://acm.hdu.edu.cn/showproblem.php?pid=4671 这个高端的题意啊 看了N久啊 n>m时  直接第一列按顺序来 第二列为M+1 else  第一列顺序 ...

  3. bzoj1196

    带有限制的生成树 首先不难想到二分答案转化为判定性问题 假设二分出了一个答案p, 首先我们先考虑建一级公路. 由于一级公路费用是大于二级公路的,所以对于那些一级公路花费<=p的道路, 不难想到让 ...

  4. BZOJ_1096_[ZJOI2007]_仓库建设_(斜率优化动态规划+单调队列+特殊的前缀和技巧)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1096 有\(n\)个工厂,给出第\(i\)个工厂的到1号工厂的距离\(x[i]\),货物数量\ ...

  5. 【转】ubuntu 12.04英文版设置成中文版

    原文网址:http://blog.csdn.net/hhbgk/article/details/7958776 适用于ubuntu 12.04英文版的系统,其它版本的设置应该是大同小异的. 进入ubu ...

  6. ES5新特性:理解 Array 中增强的 9 个 API

    为了更方便的对JS中Array进行操作,ES5规范在Array的原型上新增了9个方法,分别是forEach.filter.map.reduce.reduceRight.some.every.index ...

  7. WPF学习小记

    WPF通用控制事件 Click:当控件被单击时发生.某些情况下,当用户按下Enter键时也会发生这样的事件. Drop:当拖曳操作完成时发生,也就是说,当用户将某个对象拖曳dao该控件上,然后松开鼠标 ...

  8. LightOJ 1236 Pairs Forming LCM 合数分解

    题意:求所有小于等于n的,x,y&&lcm(x,y)==n的个数 分析:因为n是最小公倍数,所以x,y都是n的因子,而且满足这样的因子必须保证互质,由于n=1e14,所以最多大概在2^ ...

  9. [codevs3296]有序数组合并

    题目描述 Description 合并两个有序数组A和B,使得结果依然有序. 进阶:合并两个有序数组A和B,假设A有n个数,B有m个数,A数组后面还有m个空余空间,需要将结果保存在A中. 请使用O(n ...

  10. 【原】Scala学习资料

    Scala是基于JVM的一种通用函数式也是面向对象编程语言,能和Java.C#等主流的编程语言无缝融合. 下面给大家推荐一些Scala的学习资料,序号靠前的重要性比较高. 1.Scala初学指南 (1 ...