美文网首页Swiftswift-问题栈iOS高级应用
Swift2.0下UICollectionViews拖拽效果的实

Swift2.0下UICollectionViews拖拽效果的实

作者: 过客又见过客 | 来源:发表于2015-07-29 17:10 被阅读4271次

原文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工程师们的付出!

可加群一起交流共同学习:801216530。

相关文章

  • Swift2.0下UICollectionViews拖拽效果的实

    原文UICollectionViews Now Have Easy Reordering。本着好东西要分享的原则以...

  • 拖拽,碰撞检测

    1. 拖拽 1.1 拖拽原理 鼠标拖拽效果的实现,就是在鼠标摁下和移动的时候,修改元素的定位值的效果。 1.1.1...

  • HTML5实现拖拽

    实现拖拽效果源元素 - 要拖拽的文件目标元素 - 要拖拽到哪里去 目前实现拖拽效果使用原生DOM就能实现 - 最麻...

  • 第一个Xcode项目(2) - Auto Layout - 布局

    [相关信息:Xcode7.2 ; Swift2.0] 先回顾一下效果图 之前我们完成了Scroll View和内容...

  • unity 实现物体缓慢移动(move slowly)

    目标:实现类似拖拽后“飘回”原位的效果 我们知道改变物体的位置可以通过对transform.position赋值实...

  • 拖拽API

    实现拖拽效果 目前实现拖拽效果 HTML5拖拽 源元素事件例子 目标元素事件 从本地拖放图片到页面中 实现拖拽

  • 百度地图——maker(动画)

    禁止拖拽地图 map.disableDragging();//禁止拖拽 maker的动画效果:自定义图片(动画效果...

  • web前端-原生鼠标拖拽效果

    鼠标拖拽效果分为3个事件 鼠标按下事件onmousedown, 事件源是点击的对象, 就是我们要拖拽的对象 鼠标移...

  • JS实现拖拽功能

    拖拽功能是我们日常项目中常用的效果,今天我们就来研究一下如何实现简单的拖拽功能。想实现拖拽功能其实很简单,主要需要...

  • 移动拖拽效果

    移动拖拽效果

网友评论

  • 王不饿呀:楼主,我写的不是瀑布流,但是拖动cell时候cell的size不变怎么办呢,下载了他的源码就改了布局发现他的size是变化的。
    王不饿呀:不是的楼主,我没写那么复杂,就是和secondView一样,我给他加了 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {} 这个属性后他cell的size是变化的,而我却不行,
    过客又见过客:@王大大大大人 请查看ThirdViewController.swift这个文件中,最后几行代码的实现,重写invalidationContextForInteractivelyMovingItems这个方法
  • 1aace356ed00:楼主,我问下view跟着手势一起移动是怎么实现的呢?能说下大概的思路吗? :pray: 谢谢楼主~
  • 雨雪传奇:楼主,为啥我拖动的时候在不停的抖动 :worried:
    雨雪传奇:@雨雪传奇 好吧, 是没有在 begin 状态时 判断 selecetedIndexPath 是否为 nil
    就当我啥也没说 :yum:
    最后感谢楼主的分享 :smile:
    雨雪传奇:@雨雪传奇 楼至,抖动的问题没有了,因为我把手势加在了 cell 上. 但是又出现了新的问题, 长按 cell 移动是没有问题的, 但为什么 点击collectionView 的其他区域就崩溃了

本文标题:Swift2.0下UICollectionViews拖拽效果的实

本文链接:https://www.haomeiwen.com/subject/mswmqttx.html