• 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是可以移动的。


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

  • 相关阅读:
    省市区distpicker,从数据库里查出来回显,动态绑定
    ajax请求里面的success和error里面的layer.msg,status: "parsererror",刷新父界面,碰到的一些问题
    排序算法时间和空间算法度
    适配器模式
    守护线程
    工厂模式之简单工厂模式、工厂模式、抽象工厂
    ArrayList源码分析和缩减版手写ArrayList(jdk1.8和1.9)
    HashMap排序题
    二进制中1的个数
    anaconda指定镜像源,解决conda下载速度慢失败问题
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5329715.html
Copyright © 2020-2023  润新知