【SwiftUI】onMove時の destination の値が並び替え方向の違いで異なる謎。

先日の記事で List の並び替えについて onMove アクションについて紹介しました。onMove の引数 perform: には以下のフォーマットのメソッドを指定する必要があることも説明しました。

(from source: IndexSet, to destination: Int) -> Void

リストの並び替えを完了するだけであれば、リストの元データ配列の move メソッドに sourcedestination を渡せば済みます。

self.(配列).move(fromOffsets: source, toOffset: destination)

ただし、データの並び順を各データごとに持たせたりする場合、並び替え後にsource と destination を使って全体の並び順データを更新する必要があります。

筆者も開発中にその必要性が生じたので、source と destination にどのような値が入っているか調べてみました。

source と destination の値を確認してみる

先日の記事で使った物と同じですが、以下のコードで試してみました。

struct ContentView: View {
    
    @State private var languages = [
        "C/C++",
        "Java",
        "C#",
        "Ruby",
        "PHP",
        "Objective-C",
        "Swift",
        "Kotlin",
        "Python"
    ]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(languages, id: \.self) { lang in
                    Text(lang)
                }
                .onMove(perform: move)
            }
            .navigationBarItems(trailing: EditButton())
        }
    }
    
    func move(from source: IndexSet, to destination: Int) {
        self.languages.move(fromOffsets: source, toOffset: destination)
        print("source:\(source.first!)")
        print("destination:\(destination)")
    }
}

print で source と destination の値を出力しました(source は IndexSet 型なので first から取得します)。

まず、上から下へ並び替えた場合です。

リストを0始まりとして考えた場合、source の方は 2 なので画面上の順と一致します。しかし、destination は 6 なので画面上の順と一致しません。

「そうか、destination は 1始まりのルールなのだな」とこの時点ではそう思いました

次に下から上への並び替えです。

おや?今度は source も destination も0始まりのように見えます。

同様に、いろいろなパターンを試してみましたが、何度やっても、上から下に並び替えする場合と下から上に並び替えする場合で、destination だけ値が異なりました。

理由を調べたり考えたりしてみましたが、どうもよくわからず仕舞いです。

0始まりの値として考えたかったので、仕方なく、上から下へ並び替えする場合は destination を -1 することで対処しています。

バグなのか、はたまたちゃんと意味があるのか、どなたかご存知の方がいらっしゃいましたらコメント下さい

以上