[SwiftUI]List Itemを常に移動可能にする

SwiftUIにおいて、リストに登録したアイテムを移動するViewを実装したかったのですが、toolbarのEditButtonを用いる実装では都度Editボタンをクリックする必要があり、その手間を省けないかと思いました。実装してみれば簡単なのですが、あまりサンプルも転がっていなかったようなので、ご紹介します。

環境:Xcode 12.4, Swift 5

スタート地点

まずは、単純なリストを実装するところから始めます。

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        SampleList()
    }
}

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {
        ContentView()
    }
}

単純に5つのアイテムを生成し、リストに表示するシンプルなリストです。

SampleList.swift

struct SampleList: View {

    struct Item: Identifiable {
        let id = UUID()
        let title: String
    }

    @State private var items: [Item] = (0..<5).map { Item(title: "Item #\($0)") }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text(item.title)
                }
            }
        }
    }
}

EditButtonを用いて、リストアイテムを移動する

まずは、EditButtonを用いた実装を紹介します。なお、ここでNavigationViewを追加していますが、追加せずとも動作するはずです。

以下のサイトを参考に、下記を追記しています。

  • move functionを定義
  • リストアイテムのonMoveのPerformアクションのClosureとして指定
  • リストのModifierにtoolbarを追加し、要素としてEditButtonを追加

https://developer.apple.com/documentation/swiftui/editbutton

SampleList.swift

struct SampleList: View {

    struct Item: Identifiable {
        let id = UUID()
        let title: String
    }

    @State private var items: [Item] = (0..<5).map { Item(title: "Item #\($0)") }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text(item.title)
                }
                .onMove(perform: move)
            }
            .navigationTitle("Navigation Title")
            .toolbar {
                EditButton()
            }
        }
    }
    
    private func move(source: IndexSet, destination: Int) {
        items.move(fromOffsets: source, toOffset: destination)
    }
}

Editボタンをクリックすることで、編集モードになり、リストが移動できるようになります。

EEdEduEdE

リストアイテムを常に移動できるようにする

UIによっては、都度Editボタンをクリックするのが煩雑な場合もあるかと思います。次に紹介するのは、リストのアイテムが常に移動できるようになるサンプルです。

以下のサイトを参考に、以下を実装します。

  • リストのenvironment Modifierの中で、EditMode.activeを指定

https://developer.apple.com/documentation/swiftui/environmentvalues
https://developer.apple.com/documentation/swiftui/editmode

SampleList.swift

struct SampleList: View {

    struct Item: Identifiable {
        let id = UUID()
        let title: String
    }

    @State private var items: [Item] = (0..<5).map { Item(title: "Item #\($0)") }
    
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Text(item.title)
                }
                .onMove(perform: move)
            }
            .navigationTitle("Navigation Title")
            .environment(\.editMode, .constant(EditMode.active))
        }
    }
    
    private func move(source: IndexSet, destination: Int) {
        items.move(fromOffsets: source, toOffset: destination)
    }
}

このように、Editボタンをクリックすることなく、リストのアイテムが常に移動可能となります。

まとめ

SwiftUIはドキュメント自体は充実しているのですが、サンプルがないため理解に時間がかかってしまいます。こういった小さなサンプルがなにかの役に立てれば幸いです。

その他参考にしたサイト

https://www.vadimbulavin.com/add-edit-move-and-drag-and-drop-in-swiftui-list/

https://www.hackingwithswift.com/quick-start/swiftui/how-to-let-users-move-rows-in-a-list