(2021/09/26 更新)
Listで生成したデータ一覧の行削除処理について解説します。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.5
【iOS】15.0
【macOS】Big Sur バージョン 11.6
前提条件
行削除ができるのは、次の条件を満たしたリストです。
- リストに関連付けられたコレクション(配列)が、@State等で宣言されており、更新可能である。
- List + ForEach でリストが生成されている。
→行削除処理はForEachのModifierを使って実現します。 - ForEachは範囲指定ではなくデータ指定による繰り返し。
→各行の一意性が確保されている必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct ContentView: View { /// ①対象のコレクション(配列)が更新可能 @State private var fruits = ["りんご", "オレンジ", "バナナ", "パイナップル", "いちご"] var body: some View { /// ②List+ForEachでリストを生成 List { /// ③データ指定による繰り返し ForEach(fruits, id: \.self) { fruit in Text(fruit) } } } } |
行削除処理の追加
ForEachの ModifierであるonDelete()で行削除時に呼び出すメソッドを指定します。
呼び出し方は"(削除対象:IndexSet) -> Void"の形式になります。
呼び出されるメソッド(rowRemove)には、コレクション(配列)から要素を削除する処理を記述します。
この中で使用しているremove(atOffsets:)は、RangeReplaceableCollectionプロトコルで宣言されている,
iOS13以降でのみ使用可能なメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct ContentView: View { @State private var fruits = ["りんご", "オレンジ", "バナナ", "パイナップル", "いちご"] var body: some View { List { ForEach(fruits, id: \.self) { fruit in Text(fruit) } /// 行削除操作時に呼び出す処理の指定 .onDelete(perform: rowRemove) } } /// 行削除処理 func rowRemove(offsets: IndexSet) { fruits.remove(atOffsets: offsets) } } |
対象行をスワイプするだけで、行削除が可能になります。
編集モードの切り替えボタン追加
リストを編集モードに切り替えると、スワイプとは異なる操作で行削除が行えます。
以下は編集モードの切り替えをするEditButtonをナビゲーションボタンとして配置した例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
struct ContentView: View { @State private var fruits = ["りんご", "オレンジ", "バナナ", "パイナップル", "いちご"] var body: some View { NavigationView { List { ForEach(fruits, id: \.self) { fruit in Text(fruit) } .onDelete(perform: rowRemove) } .toolbar { EditButton() } } } func rowRemove(offsets: IndexSet) { fruits.remove(atOffsets: offsets) } } |
Editボタンを押して編集モードにすると、次のようにタップで削除行を選べるようになります。
スワイプによる削除を止める
スワイプによる操作はユーザーが意図せず行を削除してしまう可能性が少なからずあります。
スワイプによる削除を止めて、編集モードでのみ削除できるようにする方法を紹介します。
以下は環境変数のeditModeを参照し、編集モードの場合のみ削除機能を有効にする例です。
編集モードで無い場合は、.onDelete()にnilを渡して削除処理を無効にしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct ContentView: View { @State private var fruits = ["りんご", "オレンジ", "バナナ", "パイナップル", "いちご"] @State var editMode: EditMode = .inactive var body: some View { NavigationView { List { ForEach(fruits, id: \.self) { fruit in Text(fruit) } .onDelete(perform: editMode.isEditing ? rowRemove : nil) } .toolbar { EditButton() } .environment(\.editMode, $editMode) } } func rowRemove(offsets: IndexSet) { fruits.remove(atOffsets: offsets) } } |
NavigationViewで編集モードが取得できない問題がある為、独自に編集モードを保持するeditModeプロパティを環境変数に設定しています。
編集モードとNavigationの関係については、こちらの記事を参照してください。