[Swift] UITableViewの編集モードを制御して「削除のみ許可」，「ソートのみ許可」を実現する

UITableViewには編集モードというものがあります．
基本的な使い方は以下を参照していただければ理解できるかと思います．
・[UITableView の編集モードを利用してデータの削除や並び替えを行う](http://himaratsu.hatenablog.com/entry/tableview/editmode)


編集可能なCellの指定は`UITableViewDataSource`のメソッドで行います．

```swift
// 1. 編集モードを許可するIndexPathの指定
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
// 2. ソートを許可するIndexPathの指定
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool
```

1.で`true`のCellは，**削除が可能**になり，2.の**ソートの可否指定が有効**になります．
2.で`true`のCellは，**ソートが可能**になります．(1.でtrueの場合のみ)

つまり，
<h4>**ソートのみ可能**が標準でできない！！！</h4>

　
まあ利用頻度は少ないとは思いますが，強引にやってみたので共有します．


# 編集モード中の挙動
まず最初に，`tableView.editing = true`にした時の，設定別の挙動について見てみます．

#### ・`canEdit->false`, `canMove->false`
![スクリーンショット 2016-02-14 16.17.12.png](https://qiita-image-store.s3.amazonaws.com/0/93219/1b0a8217-b647-b652-f71f-0c6b5161b483.png)
どちらも`false`のときは，当然何も起きません．

#### ・`canEdit->true`, `canMove->false`
![スクリーンショット 2016-02-14 16.17.38.png](https://qiita-image-store.s3.amazonaws.com/0/93219/673040f4-e241-1995-eb0a-2b2b0f28a18e.png)
最初に説明したように，削除のみ可能となっています．

#### ・`canEdit->false`, `canMove->true`
![スクリーンショット 2016-02-14 16.17.12.png](https://qiita-image-store.s3.amazonaws.com/0/93219/1b0a8217-b647-b652-f71f-0c6b5161b483.png)
この場合は，`tableView:canEditRowAtIndexPath:`が許可されていないので，何も起きません

#### ・`canEdit->true`, `canMove->true`
![スクリーンショット 2016-02-14 16.22.23.png](https://qiita-image-store.s3.amazonaws.com/0/93219/85293ca5-0787-42e7-6c3d-6ec3041fbe8a.png)
ソートを行うには両方を`true`にする必要がありますが，画像のように削除も可能となってしまいます．

# 編集モード中の画面構成
編集モード中は，(少し見づらいですが)下の画像のように，`UITableViewCell`の`ContentView`が縮小され，両端に`UITableViewCellEditControl`というものが追加されています．
<img src="https://qiita-image-store.s3.amazonaws.com/0/93219/96a9d6e0-e676-b391-7abb-647fbb52d6aa.png" width=700>


# 削除ボタンを隠す
実際，隠すとなると，方法はいくつかあるかもしれませんが，一番シンプルにできそうなものを紹介します．
どうするかといいますと，テーブルビューの幅を大きくしてやることで，削除ボタンを画面外におしやります．
それだけかよ，と思われた方，すみませんm(._.)m笑

幅をどのように変えるかですが，UITableViewの左余白の`NSLayoutConstraint`を調節してやります．
余談ですが，**他の場合でも`frame`を調整するよりは，`NSLayoutConstraint`を調整した方がコード量も減りますし，調整しやすい場合が多いかと思います．**(と，最近感じます)

`UITableViewCell`の`ContentView`は，編集モードに入る時，(どちらも`true`で)両端が`38px`縮みます．
ですので，左余白の`NSLayoutConstraint`をその分マイナスしてやれば，うまく隠れてくれます．

テキストだけでは分かりづらいので，主要部分のコードを載せます．

```swift
// 編集タイプ
enum EditType {
  case None, All, Delete, Sort
}

var editType = EditType.None {
  willSet {
    self.tableView.editing = true
    self.leftConstraint.constant = 0
    if newValue == .None { self.tableView.editing = false }
    else if newValue == .Sort { self.leftConstraint.constant = -38 }
    self.tableView.reloadData()
  }
}

/**
 *  UITableView DataSource --------
 */
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
  switch self.editType {
    case .None : return false
    default : return true
  }
}
    
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
  switch self.editType {
    case .None, .Delete : return false
    default : return true
  }
}
```
`editType`の値を変えてやることで，編集モードが切り替わるようになっています．

# 実行結果
上記コードで，`self.editType = .Sort`とした時の実行結果です．
![スクリーンショット 2016-02-14 16.23.09.png](https://qiita-image-store.s3.amazonaws.com/0/93219/ede0f81e-d479-8876-8c84-848a37243c46.png)
はい，うまく隠すことができましたね．

# 注意点
**削除(ソート)をさせたくないCellがある場合**は一工夫入ります．
TableView上の上2つを固定させたい時の例を示します．

```swift
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
  switch self.editType {
    case .None : return false
    case .Sort : return true  // ここ重要
    default :
      if indexPath.row < 2 { return false }
      else { return true }
  }
}
    
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
  switch self.editType {
    case .None, .Delete : return false
    default :
      if indexPath.row < 2 { return false }
      else { return true }
  }
```
一見普通の処理に見えますが，コメント部分のように，**`.Sort`のときは`true`を返す**ようにします．
今回の場合，ソートのみ可能とする場合に`NSLayoutConstraint`をいじっていますので，この処理を加えないと固定したCellが見切れてしまいます．

# 最後に
今回は，ソートのみ可能のTableViewを実装してみました．(制御という割にはベタな方法でしたが，，，)
Header, Footerがある場合は，フレームの調整が必要になりますが，シンプルに実装することができました．
今回のように，様々な状況に対応できるようなエンジニアになりたいですね．

それではよいiOSライフを！！！！