[iOS 11] iOS 11でUITableViewDelegateに追加されたメソッドを使ってスワイプアクションを実装する

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

UITableViewDelegateにスワイプアクション用のメソッドが追加された

iOS 11ではUITableViewDelegateに以下のスワイプアクション用のメソッドが追加されました。

// Swipe actions
// These methods supersede -editActionsForRowAtIndexPath: if implemented
// return nil to get the default swipe actions
@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?

@available(iOS 11.0, *)
optional public func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?

leadingSwipe〜は左から右にスワイプ、trailingSwipe〜は右から左にスワイプした場合に表示するアクションを返すメソッドです。 リファレンスを見ると、「文章を左から右に読む言語環境の場合」という説明があったので、アラビア語のように右から左に読む環境だと逆になるのかと思って試してみましたが、変わりませんでした(´・ω・`)

ともあれ上記メソッドで1つ1つのアクションに対応したUIContextualActionの集合であるUISwipeActionsConfigurationを返してあげればスワイプ時にメニューが表示されます。

大きな変更点といえば

  • スワイプ時にセルの左側にもボタンを表示できるようになった
  • アクションに画像を表示できるようになった

ことではないでしょうか。

下記は右から左にスワイプした時に1つのアクションを表示する場合の実装です。ポイントはタップ時のアクションを実行したらcompletionHandlerを実行することです。
引数のBool値は、アクションを実行した場合はtrueを、何らかの理由でアクションが実行できなければfalseを指定します。

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let action = UIContextualAction(style: .normal,
                                    title: "normal") { (action, view, completionHandler) in
                                      // 何らかのアクション(処理)を実行

                                      // 処理の実行結果に関わらず completionHandler を呼ぶのがポイント
                                      completionHandler(true)
    }
    let configuration = UISwipeActionsConfiguration(actions: [action])
    return configuration
}

検証環境

本エントリは以下の環境で検証を行っています。

  • macOS Sierra バージョン 10.12.6
  • Xcode Version 9.0.1 (9A1004)
  • Swift 4
  • iPhone X シミュレーター iOS 11.0

UIContextualAction.Styleのnormalとdestructiveの違い

UIContextualAction.Styleのnormalとdestructiveで何が違うのか以下のコードで確認しました。 コード上の違いはtitleとstyleだけです。

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let normalAction = UIContextualAction(style: .normal,
                                    title: "normal") { (action, view, completionHandler) in
                                        completionHandler(true)
    }
    let destructiveAction = UIContextualAction(style: .destructive,
                                               title: "destructive") { (action, view, completionHandler) in
                                                completionHandler(true)
    }
    let configuration = UISwipeActionsConfiguration(actions: [normalAction, destructiveAction])
    return configuration
}

実行結果はこちらです。右から左にスワイプすると定義したメニューが表示されました。
normalの方はタップすると閉じるだけですが、destructiveの方は削除されました。
※ 見た目上削除されたように見えるだけでUITableViewのデーターソースとなるモデルオブジェクトが削除されるわけではありません。モデルオブジェクトは自分で削除する必要がありますのでご注意ください。

また、左に目一杯スワイプ(フルスワイプと言います)しても何も起きませんね。

normal_and_destructive_1

フルスワイプ時にデフォルトでUISwipeActionsConfigurationの1番目のアクションが実行される

続いて以下のようにActionの順番を入れ替えてみるとどうでしょうか。

// Actionの順番を逆にする
let configuration = UISwipeActionsConfiguration(actions: [destructiveAction, normalAction])

normal_and_destructive_2

それぞれのアクションタップ時の挙動は変わりませんが、フルスワイプした時に削除されました。
これはUISwipeActionsConfigurationのプロパティ、performsFirstActionWithFullSwipe(フルスワイプ時に1番目のActionが実行するかどうか)がデフォルトでtrueになっているため1番目のActionであるdestructiveActionが実行されたためです。performsFirstActionWithFullSwipeをfalseにするとフルスワイプしてもActionは実行されません。

また、左に目一杯スワイプ(フルスワイプと言います)しても何も起きませんね。

実は、normalが1番目のActionだった時もアクションは実行されていたのでした。

UIContextualActionのカスタマイズ性

UIContextualActionはstyleとtitleの他に以下の設定で外観がカスタマイズ可能です。

  • backgroundColor
    • 背景色
    • デフォルト値はstyleに応じた色が設定されるが、明示的に本プロパティに色指定すればそれが優先される
  • image
    • アクションボタンに表示する画像
// 背景を青に
normalAction.backgroundColor = .blue

// 画像を設定
destructiveAction.image = UIImage(named: "trash")

customize_action

ちゃんと設定が反映されていますね。

おわりに

今回はiOS 11で新たに追加されたUITableViewDelegateのスワイプアクション用メソッドをご紹介しました。 このメソッドを使えばiOSのメールアプリのようなスワイプメニューを表示することが簡単に出来そうですね。

参考