CollectionViewを用いたToDoリストへの削除機能の実装

## 概要

- TableViewでは左スワイプでの削除機能が簡単に実装できるが（例：LINEのトーク削除）、CollectionViewだとそうはいかない

そこで、タスクを長押ししたら確認画面が出て”完了”にできる機能を実装する

## コード

### 1. recognizerの作成とアタッチ

```swift
override func viewDidLoad() {
	super.viewDidLoad()

	//略

    let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(deleteConfirm))
	collectionView.addGestureRecognizer(recognizer)

	//略

}
```

まずは、`viewDidLoad`内で`recognizer`を作成する。`recognizer`とはタッチやスワイプを検知するためのもので、今回は長押しを検知するので`UILongPressGestureRecognizer`を用いる。

`action`には長押しした際に実行する処理を書くことができ、今回は`deleteConfirm`というメソッドを実行させる。

また、作成した`recognizer`は`collectionView`にアタッチ（取り付け）する。この時点ではまだ`deleteConfirm`というメソッドを作成していないのでエラーが出るが気にしなくて良い。

### 2. deleteConfirmメソッドの作成

```swift
class MemoCollectionViewController: UIViewController {

	//略

	override func viewDidLoad() {
    	super.viewDidLoad()

		//略

	}

    @objc func deleteConfirm(sender: UILongPressGestureRecognizer){
        
    	print("長押しされました")
        
    }

    //略

}
```

`viewDidLoad`の下あたり（class内であればどこでも問題ない）に`deleteConfirm` メソッドを作成する。この際、先頭に`@objc` と書かないとエラーが起こるので注意。長押しされた際の処理はひとまず`print` としておき、きちんと動作するか確認する。

シミュレータで実行してみると、長押ししたときに一回、押していた指を離したときに一回`deleteConfirm` メソッドが呼び出されているのがわかる。これは`UILongPressGestureRecognizer` の特徴であり、今回は一度目の呼び出しのみを用いたいので以下のコードを`deleteConfirm` 内に付け足す。

```swift
@objc func deleteConfirm(sender: UILongPressGestureRecognizer){
        
	guard sender.state == .began else { return }
	print("長押しされました")
        
}
```

これによって、タスクが長押しされたときに一度だけ`deleteConfirm` メソッドが呼ばれるようになる。

### 3. 確認用アラートの作成と表示

```swift
@objc func deleteConfirm(sender: UILongPressGestureRecognizer){

	guard sender.state == .began else { return }
    print("長押しされました")

    let alert = UIAlertController(title: nil, message: "タスクを完了にしますか？", preferredStyle: .alert)    
	alert.addAction(
		UIAlertAction(title: "キャンセル", style: .cancel)
	)
	alert.addAction(
		UIAlertAction(title: "OK", style: .default)
	)
	present(alert, animated: true)
        
}
```

次に、確認用のアラートを作成する。ボタンはキャンセルボタンとOKボタンの２つを用意し、キャンセルボタンには`style: .cancel` 、OKボタンには`style: .default` を適用しておく。

### 4. indexPathの取得

```swift
@objc func deleteConfirm(sender: UILongPressGestureRecognizer){

	guard sender.state == .began else { return }
    print("長押しされました")

    let point: CGPoint = sender.location(in: collectionView)
    print(point)

    //略
        
}
```

まずは長押しされた位置の`collectionView` 内の座標を取得する。`CGPoint` 型の変数にはX座標とY座標が入っている。

```swift
let point: CGPoint = sender.location(in: collectionView)
print(point)
let indexPath = self.collectionView.indexPathForItem(at: point)
print(indexPath)
```

次に、先ほど取得したタッチ位置を使って、セルの`indexPath` を取得する。`indexPath` の取得には`indexPathForItem` メソッドを用いる。

タスクを長押しすると`Optional([0, 0])` や`Optional([0, 1])` のように、2つの数字の組み合わせが取得できていることを確認する。セルがないところを押すと`nil` になってしまうので、以下のコードを追加して`indexPath` が`nil` のときは処理を終わらせるようにする。

```swift
let point: CGPoint = sender.location(in: collectionView)
print(point)
let indexPath = self.collectionView.indexPathForItem(at: point)
print(indexPath)
guard let indexPath else { return }
```

### 5. タスクを完了にする処理の実装

```swift
alert.addAction(
	UIAlertAction(title: "OK", style: .default, handler: {action in
    	self.titles.remove(at: indexPath.row)
        self.contents.remove(at: indexPath.row)
        self.collectionView.reloadData()
        self.saveData.set(self.titles, forKey: "titles")
        self.saveData.set(self.contents, forKey: "contents")
    })
)
```

最後に、OKボタンを押した際にタスクを”完了”にする処理を書く。具体的には、

1. `titles` と`contents` から、該当するデータを削除する。
2. `collectionView` をリロードする。
3. `userDefaults` にデータを保存し直す。

といった手順でタスクを完了させる処理を書く。