web-dev-qa-db-ja.com

SwiftUIリスト内のアイテムを移動するときの不要なアニメーション

以下のコード例のようなSwiftUIリストがあります。

struct ContentView: View {
    @State var numbers = ["1", "2", "3"]
    @State var editMode = EditMode.inactive

    var body: some View {
        NavigationView {
            List {
                ForEach(numbers, id: \.self) { number in
                    Text(number)
                }
                .onMove {
                    self.numbers.move(fromOffsets: $0, toOffset: $1)
                }
            }
            .navigationBarItems(trailing: EditButton())
        }
    }
}

編集モードに入り、アイテムを1つ上に移動すると、アイテムをドロップした後に奇妙なアニメーションが発生します(下のgifを参照)。ドラッグしたアイテムが元の位置に戻ってから、もう一度移動先に移動したようです(アニメーション付き)

strange animation when reordering items

興味深いことに、アイテムをリストの下にドラッグしたり、1つ以上の位置を上にドラッグしたりしても、起こりません。

これは、状態のアイテムがビュー側でドラッグアンドドロップによって既に並べ替えられている場合でも、状態のアイテムが並べ替えられると、リストがアニメーションを実行するためだと思います。しかし、明らかにアイテムを1つ上に移動する以外のすべての場合にうまく処理します。

この問題を解決する方法に関するアイデアはありますか?それとも既知のバグですか?

XCode 11.4.1を使用していますが、ビルドターゲットはiOS 13.4です

(また、「実世界」アプリではCore Dataを使用しているため、アイテムを移動すると、DBでアイテムの順序が更新され、状態が更新されますが、アニメーションの問題はまったく同じに見えます。)

6
Mateusz K

これは、受け入れられた回答におけるMateusz Kのコメントに基づく解決策です。順序と数値のハッシュを組み合わせました。動的に更新される数値の代わりに複雑なオブジェクトを使用しています。これにより、基になるオブジェクトが変更された場合にリストアイテムが確実に更新されます。

class HashNumber : Hashable{
        
        var order : Int
        var number : String
        
        init(_ order: Int, _ number:String){
            self.order = order
            self.number = number
        }
        
        static func == (lhs: HashNumber, rhs: HashNumber) -> Bool {
            return lhs.number == rhs.number && lhs.order == rhs.order
        }
        //
        func hash(into hasher: inout Hasher) {
            hasher.combine(number)
            hasher.combine(order)
        }
    }
    
    func createHashList(_ input : [String]) -> [HashNumber]{
        
        var r : [HashNumber] = []
        
        var order = 0
        for i in input{
            let h = HashNumber(order, i)
            r.append(h)
            order += 1
        }
        
        return r
    }
    
    struct ContentView: View {
        @State var numbers = ["1", "2", "3"]
        @State var editMode = EditMode.inactive
        
        var body: some View {
            NavigationView {
                List {
                    ForEach(createHashList(numbers), id: \.self) { number in
                        Text(number.number)
                    }
                    .onMove {
                        self.numbers.move(fromOffsets: $0, toOffset: $1)
                    }
                }
                .navigationBarItems(trailing: EditButton())
            }
        }
    }
0
Jerry Sha