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

原文UICollectionViews Now Have Easy Reordering。本着好东西要分享的原则以及出于对个人技能的提升,在此作一个粗陋的翻译,翻译尽量保留原作内容。本文主要是基于Swift 2.0实现的一个简单的UICollectionView的cell的拖拽效果,编译环境是Xcode 7。效果虽然简单,但足够用不是吗? 对于翻译,本人也是第一次,难免有失误或错误之处,万望不吝赐教,以便及时修正。

我是UICollectionView的忠实粉丝。相对于它的兄长UITableView,UICollectionView可定制性更高,且更加灵活。时至今日,我使用UICollectionView要远多于UITableView。随着IOS9的发布,使的它的排序(即拖拽效果)更加简单。在这之前,想要通过原生控件达到开箱即用的效果,那是一件不可能的事情,如果想要达到效果势必要完成大量的工作。首先让我们重新回顾一下相关的API,然后你可以在通过Github下载示例Demo

实现简单的拖动排序效果最简单的办法就是使用UICollectionViewController。现在它有一个新增的属性installsStandardGestureForInteractiveMovement,通过添加手势对cell进行排序。这是一个BOOL类型的属性,默认为YES,并且我们需要重写一个方法以来达到我们想要的效果。

 override func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
//调整数据源数据的顺序
}

当我们重写了moveItemAtIndexPath,collectionView就认为cell是可以移动的。

1.gif

如果我们使用带有collectionView的普通UIViewController实现拖拽效果,就会变得很复杂。我们不仅要实现UICollectionViewDataSource上面提到的的代理方法,还要重写installsStandardGestureForInteractiveMovement。但是不要担心,实现起来同样简单。UILongPressGestureRecognizer长按手势,能够完全满足拖拽需求。

private var longPressGesture: UILongPressGestureRecognizer!

    override func viewDidLoad() {
super.viewDidLoad()
longPressGesture = UILongPressGestureRecognizer(target: self, action: "handleLongGesture:")
self.collectionView.addGestureRecognizer(longPressGesture)
} func handleLongGesture(gesture: UILongPressGestureRecognizer) { switch(gesture.state) { case UIGestureRecognizerState.Began:
guard let selectedIndexPath = self.collectionView.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else {
break
}
collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)
case UIGestureRecognizerState.Changed:
collectionView.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))
case UIGestureRecognizerState.Ended:
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}

当手势生效时,获取手势所在cell的indexPath,然后根据手势的不同状态调用collectionView的相关方法,具体如下:

  • beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath)开始拖拽某个cell时调用此方法,并把将被拖拽的cell的indexPath传入方法。
  • updateInteractiveMovementTargetPosition(targetPosition: CGPoint)根据手势更新被拖拽的cell的位置
  • endInteractiveMovement()手势结束时调用,结束拖拽
  • cancelInteractiveMovement()手势取消时调用,取消拖拽

这样能够实现我们想要的拖拽效果了。

2.gif

使用普通UIViewController最终达到的效果跟我们使用UICollectionViewController实现的效果是一样的。相当酷,不是吗?但是我们可以通过自定义UICollectionViewLayout使它变得更酷。下面我们来实现一个简单的瀑布流。

3.gif

啊哈,看起来相当酷,但是如果我们不想在拖拽的过程中改变cell的size,我们应该怎么做呢?被拖拽的cell在移动的过程中,应该保持size不变。这当然是可以实现的。UICollectionViewLayout为我们提供了相关方法来解决这个问题。

func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [NSIndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext

func invalidationContextForEndingInteractiveMovementOfItemsToFinalIndexPaths(indexPaths: [NSIndexPath], previousIndexPaths: [NSIndexPath], movementCancelled: Bool) -> UICollectionViewLayoutInvalidationContext

cell在起始indexPath和目标indexPath拖拽期间,会调用第一个方法。第二个方法类似,但是它仅会在拖拽结束后调用。根据这一点,我们可以通过使用一个小技巧达到我们的需求。

internal override func invalidationContextForInteractivelyMovingItems(targetIndexPaths: [NSIndexPath], withTargetPosition targetPosition: CGPoint, previousIndexPaths: [NSIndexPath], previousPosition: CGPoint) -> UICollectionViewLayoutInvalidationContext
{
var context = super.invalidationContextForInteractivelyMovingItems(targetIndexPaths, withTargetPosition: targetPosition, previousIndexPaths: previousIndexPaths, previousPosition: previousPosition)
self.delegate?.collectionView!(self.collectionView!, moveItemAtIndexPath: previousIndexPaths[0], toIndexPath: targetIndexPaths[0])
return context
}

解决方法简单直接。获取当前被拖拽的cell的起始indexPath和目标indexPath,然后调用UICollectionViewDataSource代理方法移动当前正在被拖拽的cell。

4.gif

毫无疑问,一个可以拖拽的的collectionView会带来非常棒的体验效果。特别感谢UIKit工程师们的付出!

Swift2.0下UICollectionViews拖拽效果的实现的更多相关文章

  1. 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

    学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...

  2. JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)

    转自<JS实现漂亮的窗口拖拽效果(可改变大小.最大化.最小化.关闭)>:http://www.jb51.net/article/73157.htm   这篇文章主要介绍了JS实现漂亮的窗口 ...

  3. 拖拽系列一、JavaScript实现简单的拖拽效果

        前端拖拽相关应用汇总 在现实生活中就像男孩子牵着(拖着)女朋友的手穿过马路:从马路的一端走到另一端这种场景很常见: 而在前端开发中拖拽效果也算是前端开发中应用最常见.最普遍的特效:其拖拽涉及知 ...

  4. JavaScript实现最简单的拖拽效果

    一.一些无关痛痒的唠叨 拖拽还是挺不错的一个页面效果,我个人认为,其生命力在于可以让用户自己做一些操作,所谓自定义.例如: ①浏览器标签顺序的拖拽切换 现在基本上所有的选项卡式的浏览器都有顺序拖拽切换 ...

  5. vue模块拖拽效果

    正巧在之前面试中遇到问实现拖拽效果 当时面试的时候简单回答了实现的方式与逻辑. 现在闲来无事,把这个东西实现了一下. 原理很简单,写的很方便. 数据驱动,建立一个数组,数组初始长度为1 拖动触发时,添 ...

  6. 原生js简单实现拖拽效果

    实现弹窗拖拽效果的原理是:按下鼠标并移动——拖拽移动物体,抬起鼠标——停止移动.主要触发三个事件:onmousedown.onmousemove以及onmouseup: 首先搭建结构:一个宽350px ...

  7. 超强的纯 CSS 鼠标点击拖拽效果

    背景 鼠标拖拽元素移动,算是一个稍微有点点复杂的交互. 而在本文,我们就将打破常规,向大家介绍一种超强的仅仅使用纯 CSS 就能够实现的鼠标点击拖拽效果. 在之前的这篇文章中 -- 不可思议的纯 CS ...

  8. 再谈React.js实现原生js拖拽效果

    前几天写的那个拖拽,自己留下的疑问...这次在热心博友的提示下又修正了一些小小的bug,也加了拖拽的边缘检测部分...就再聊聊拖拽吧 一.不要直接操作dom元素 react中使用了虚拟dom的概念,目 ...

  9. React.js实现原生js拖拽效果及思考

    一.起因&思路 不知不觉,已经好几天没写博客了...近来除了研究React,还做了公司官网... 一直想写一个原生js拖拽效果,又加上近来学react学得比较嗨.所以就用react来实现这个拖 ...

随机推荐

  1. java里面List和Array的区别是什么?

    java里面的List和Array的区别是什么? 1:数组是定长,list是自动增长.2:数组效率高,list效率低.总结:数组牺牲功能增加效率,list牺牲效率增加功能. http://bbs.cs ...

  2. ICSharpCode.SharpZipLib实现压缩解压缩

    最近,在项目中经常需要处理压缩和解压缩文件的操作.经过查找,发现了ICSharpCode.SharpZipLib.dll ,这是一个完全由c#编写的Zip, GZip.Tar . BZip2 类库,可 ...

  3. CentOs上搭建git服务器

    CentOs上搭建git服务器 首先安装setuptools wget http://pypi.python.org/packages/source/s/setuptools/setuptools-0 ...

  4. maven实现tomcat热部署

    1.使用maven+tomcat事项热部署 1.1修改tomcat-user.xml <role rolename="manager-gui"/> <!--man ...

  5. Swift中出现“no such module cocoa”的错误

    在Swift开发中,新建了一个UIViewController的子类出现“No such module 'Cocoa' 的错误, 头部是import cocoa.. 原因很简单:在建立新的File文件 ...

  6. Python正则表达式一

    推荐 http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html#!comments 这篇博客超好,建议收藏. 不过对于正则表达式小白,他没 ...

  7. 网易JS面试题与Javascript词法作用域说明

    调用对象位于作用域链的前端,局部变量(在函数内部用var声明的变量).函数参数及Arguments对象都在函数内的作用域中--这意味着它们隐藏了作用域链更上层的任何同名的属性. 2010年9月14日, ...

  8. CURL请求接口

    private function  http_curl($url,$data=null){ //1.初始化,创建一个新cURL资源                 $ch = curl_init(); ...

  9. php函数整理

    php usleep() 函数延迟代码执行若干微秒. unpack() 函数从二进制字符串对数据进行解包. uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID. time_sleep_ ...

  10. How to install ffmpeg,mp4box,mplayer,mencoder,flvtool2,ffmpeg-php on centos

    1. Enable RPM Fusion yum repository The CentOS rpm packages of ffmpeg, mplayer, mencoder and MP4Box ...