Swift2.0下UICollectionViews拖拽效果的实现
原文链接: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是可以移动的。
![](http://upload-images.jianshu.io/upload_images/268932-e214fbe97e9525b8.gif?imageView2/2/w/1240)
如果我们使用带有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()
手势取消时调用,取消拖拽
这样能够实现我们想要的拖拽效果了。
![](http://upload-images.jianshu.io/upload_images/268932-a39f7f6fd148fae6.gif?imageView2/2/w/1240)
使用普通UIViewController
最终达到的效果跟我们使用UICollectionViewController
实现的效果是一样的。相当酷,不是吗?但是我们可以通过自定义UICollectionViewLayout
使它变得更酷。下面我们来实现一个简单的瀑布流。
![](http://upload-images.jianshu.io/upload_images/268932-415262a31c3ff179.gif?imageView2/2/w/1240)
啊哈,看起来相当酷,但是如果我们不想在拖拽的过程中改变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。
![](http://upload-images.jianshu.io/upload_images/268932-240c40712b0b1f73.gif?imageView2/2/w/1240)
毫无疑问,一个可以拖拽的的collectionView会带来非常棒的体验效果。特别感谢UIKit工程师们的付出!
相关文章
- Android 使用Toolbar+DrawerLayout快速实现仿“知乎APP”侧滑导航效果
- vue和mint-ui loadMore 实现上拉加载和下拉刷新
- 所见即所得,实现一个有趣的动画效果
- 手把手实现腾讯qq拖拽删去效果(一)
- android标题栏上面弹出提示框(二) PopupWindow实现,带动画效果
- java实现第三届蓝桥杯拼音字母
- java实现第三届蓝桥杯填算式
- java实现三角螺旋阵
- DrawerLayout实现网易新闻抽屉效果
- 使用css3伪元素制作时间轴并且实现鼠标选中高亮效果
- jquery实现多行文字图片滚动效果
- python实现基于两张图片生成圆角图标效果的方法
- EasyPusher/EasyDarwin/EasyPlayer实现手机直播版本及效果整理
- 使用HTML5实现刮刮乐效果
- Android竖屏模式实现横屏效果
- vue.js3:使用clipboard.js实现复制到剪贴板(vue@3.2.37 / clipboard@2.0.11)
- 《安富莱嵌入式周报》第301期:ThreadX老大离开微软推出PX5 RTOS第5代系统,支持回流焊的自焊接PCB板设计,单色屏实现多级灰度播放视频效果
- “前.NET Core时代”如何实现跨平台代码重用 ——程序集重用
- 用纯CSS实现3D立方体效果
- SAP 电商云 Spartacus UI Site Context 模块里 Providers 组件的实现明细
- CSS实现水波纹效果示例
- 使用 Pinia 轻松实现复杂的 Vue 3 状态管理
- 【注解】Java中的元注解、内置注解、自定义注解的介绍及使用、利用反射机制和注解实现简单swagger案例
- Android 9.0 Launcher3单层高斯模糊(毛玻璃)效果的实现
- 【Python代码实现】兔年新年快乐——送给女朋友
- Qt如何实现交互效果附带代码
- SpriteBuilder&Cocos2D使用CCEffect特效实现天黑天亮过度效果
- qt之实现QMessageBox定时关闭效果
- java两种实现二分查找方式
- 165:vue+openlayers 利用turf实现遮罩挖洞效果
- 5.4.1 使用堆算法实现优级队列
- HarmonyOS鸿蒙学习笔记(15)Swiper实现抖音切换视频播放效果
- 基于WPF实现简单放大镜效果
- WPF中实现动画的几种效果(最基础方式)
- 【前沿技术RPA】 一文学会用UiPath实现自动过滤电子邮件(Email Automation)