Tana Gone
Tana Gone
1 min read

Categories

Drag’N’Dropで配列に要素を追加したり、要素を削除したり、要素の順番を変える方法を探る。以下のコードではalart Dialogの出し方も記載されている。

SwiftUI TableでRowをDrag&Dropする SmallDeskSoftware

【SwiftUI】Listの行削除 カピ通信

ArArange

コード解説

要素の移動、削除の際に呼び出されるonMove, onDeleteに引数で渡ってくる値、引数に渡さないといけない値は全く異なる。同じにしておいてくれれば良いのだが。onDeleteでは削除後にCallbackされるメソッドを引数に与える。メソッドにセットされる引数はListのIndex番号(IndexSet型)だ。onDeleteはListのCellを2本指Swipeすれば呼び出せるのだが、ContextMenuを使う方が簡単だ。ContextMenuはListにセットするのではなくListに表示されるCellにセットする。

要素の追加にはalart Dialogを使うのだが、ListにセットしてもListを格納したVStackにセットしても機能するようだ。alart Dialog表示/非表示の制御にはView状態管理変数@State変数を使えば良い。alart Dialogで受け取ったItem構造体のメンバー(タイトル、値)がViewに伝わる様にView状態管理変数@Stateが同じく使われる。

追加によって要素がWindow Sizeを超えて表示されない場合でもScroll Barが自動で付加されるのが嬉しい。

import SwiftUI

struct Item: Identifiable, Equatable {
  let id = UUID()
  let title:String
  let value: Int
}

struct ContentView: View {
  @State var listItems = [Item(title: "Item1", value: 9), Item(title: "Item2", value: 12), Item(title: "Item3", value: 15)]
  @State private var showingAddDialog = false
  @State private var newTitle: String = ""
  @State private var newValueText: String = ""

  var body: some View {
    VStack {
      List {
        ForEach(listItems, id: \.id) { item in
          HStack {
            Text(item.title)
            Text("\(item.value)")
          }
          .frame(height: 30)
          .contextMenu {
            Button(role: .destructive) {
              if let index = listItems.firstIndex(of: item) {
                listItems.remove(at: index)
              }
            } label: {
              Label("Delete", systemImage: "trash")
            }
          }
        }
        .onMove { indexSet, dest in
          listItems.move(fromOffsets: indexSet, toOffset: dest)
        }
        .onDelete(perform: deleteItems)
      }
      .animation(.default, value: listItems)
      .toolbar {
          Button {
            newTitle = ""
            newValueText = ""
            showingAddDialog = true
          } label: {
            Image(systemName: "plus")
          }
      }
      .navigationTitle("Items")
      .alert("Add Item", isPresented: $showingAddDialog) {
        TextField("Title", text: $newTitle)
        TextField("Value", text: $newValueText)
        Button("Add") {
          if let intValue = Int(newValueText.trimmingCharacters(in: .whitespacesAndNewlines)), !newTitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            listItems.append(Item(title: newTitle, value: intValue))
          }
        }
        Button("Cancel", role: .cancel) { }
      } message: {
        Text("Enter a title and a numeric value.")
      }
    }
  }
  func deleteItems(at offsets: IndexSet) {
    listItems.remove(atOffsets: offsets)
  }
}

Listを使わずForEachだけでDrag’N’Dropが出来るのだが実装が複雑そうだ。

【SwiftUI】シンプルにドラッグで並び替えできる List を 作りたい #Swift - Qiita