[Swift] CollectionViewは、簡単にドラッグ&ドロップで移動できる？

# はじめに
iOS9以上のみのサポートですが、
CollectionViewCellを簡単に移動できるメソッドがあることを最近知りましたので、
共有します。（遅）

## 動作イメージ
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/56176/028ac75f-dbea-d0ab-1751-2b2be9b84223.gif "output.gif")

## サンプルコード
まずは、サンプルコードを御覧ください。
CollectionViewの実装自体は、特別なことをしていません。
ロングタップのジェスチャー部分に注目してください。

```ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    private var numbers = [Int]()

    override func viewDidLoad() {
        super.viewDidLoad()

        numbers = loadTestData()
        addEventListner()
    }

    //ダミーデータです
    private func loadTestData() -> [Int]{

        for i in 1...100 {
            numbers.append(i)
        }
        return numbers
    }

    private func addEventListner() {
        let longPressGesture = UILongPressGestureRecognizer(target: self,
                                                            action: #selector(self.handleLongGesture(_:)))
        collectionView.addGestureRecognizer(longPressGesture)
    }

    //ここがポイントです
    func handleLongGesture(gesture: UILongPressGestureRecognizer) {

        switch(gesture.state) {

        case UIGestureRecognizerState.Began:
            guard let selectedIndexPath = collectionView.indexPathForItemAtPoint(gesture.locationInView(collectionView)) else {
                break
            }
            collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath)

        case UIGestureRecognizerState.Changed:
            collectionView.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!))

        case UIGestureRecognizerState.Ended:
            collectionView.endInteractiveMovement()

        default:
            collectionView.cancelInteractiveMovement()
        }
    }
}

extension ViewController: UICollectionViewDataSource {

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return numbers.count
    }

    func collectionView(collectionView: UICollectionView,
                        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(LabelCollectionViewCell.reuseIdentifier,
                                                                         forIndexPath: indexPath) as! LabelCollectionViewCell
        cell.number = numbers[indexPath.item]
        return cell
    }

    func collectionView(collectionView: UICollectionView,
                        moveItemAtIndexPath sourceIndexPath: NSIndexPath,
                        toIndexPath destinationIndexPath: NSIndexPath) {

        let tempNumber = numbers.removeAtIndex(sourceIndexPath.item)
        numbers.insert(tempNumber, atIndex: destinationIndexPath.item)
    }
}
```

UILabelだけのCollectionViewCellです。

```LabelCollectionViewCell.swift
import UIKit

protocol ReusableView: class {}

extension ReusableView where Self: UIView {
    static var reuseIdentifier: String {
        return String(self)
    }
}

class LabelCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var numberLabel: UILabel!
    var number = 0 {

        didSet {
            numberLabel.text = "\(number)"
        }
    }
}

extension LabelCollectionViewCell: ReusableView {}


```

### ちょっと解説
ドラッグ&ドロップの機能は、ロングタップのジェスチャーと連動して、
下記のメソッドを呼ぶだけです。

| メソッド名| 説明 |
|---|---|
| beginInteractiveMovementForItemAtIndexPath(indexPath: NSIndexPath) | 移動開始 |  
| updateInteractiveMovementTargetPosition(targetPosition: CGPoint) | 移動中 |
| endInteractiveMovement() | 移動終了|
| cancelInteractiveMovement() | 移動の取り消し |

# まとめ
iOS10の正式リリース間近ですが、
iOS9でも知らない機能がまだまだあるようです。

iOS8までは、ドラッグ&ドロップの実装は大変でしたが、
iOS9では簡単にできるようになっていたようです。（遅 2回目）

iPhone標準カレンダーのような実装ができるように鍛錬したいと思います。