matchedGeometryEffect 并不总是为位置变化设置动画

matchedGeometryEffect not always animates the position changes

我的目标是模拟一个棋子从一个方格跳到另一个方格。

struct MyView: View {
    @State var current = 0;
    @State var colors : [Color] = [.blue, .gray, .red]
    @Namespace var animationNamespace : Namespace.ID
    var body : some View {
        HStack(spacing: 12){
            ForEach(colors.indices) { i in
                ZStack{
                    RoundedRectangle(cornerRadius: 8)
                        .fill(colors[i])
                        .frame(width: 50, height: 50)
                    Image(systemName: "person.crop.square")
                        .resizable()
                        .scaledToFit()
                        .cornerRadius(8)
                        .opacity(current == i ? 1.0 : 0.0)
                        .frame(width: 50, height: 50)
                        .matchedGeometryEffect(id: current == i ? -1 : i , in: animationNamespace)
                        .onTapGesture {
                            withAnimation(.easeInOut){
                                current = (current + 1) % colors.capacity
                            }
                        }
                }
            }
        }
    }
}

单击 1 - 从位置 0 到位置 1:确定
点击 2 - 从位置 1 到位置 2 : OK
点击 3 - 从位置 3 到位置 0 : KO
单击 4 - 从位置 0 到位置 1 : OK
点击 5 - 从位置 1 到位置 2 : OK
点击 6 - 从位置 3 到位置 0 : KO
点击 7 - 从位置 0 到位置 1 : OK
...

https://developer.apple.com/documentation/swiftui/view/matchedgeometryeffect(id:in:properties:anchor:issource:)

If inserting a view in the same transaction that another view with the same key is removed, the system will interpolate their frame rectangles in window space to make it appear that there is a single view moving from its old position to its new position.

我是不是漏掉了什么?
有什么限制吗?

Xcode 版本 12.1 (12A7403) iOS14.0

此处稍作修改即可生效。不是将图像添加到所有 3 个具有不同不透明度的方块,而是仅在当前包含 pawn 的方块上绘制图像。使用 if current == i { } 执行此操作。如果你这样做,那么你可以只使用 1 作为 matchedGeometryEffect id.

struct ContentView: View {
    @State var current = 0;
    @State var colors : [Color] = [.blue, .gray, .red]
    @Namespace var animationNamespace : Namespace.ID
    var body : some View {
        HStack(spacing: 12){
            ForEach(colors.indices) { i in
                ZStack{
                    RoundedRectangle(cornerRadius: 8)
                        .fill(colors[i])
                        .frame(width: 50, height: 50)
                    if current == i {
                        Image(systemName: "person.crop.square")
                            .resizable()
                            .scaledToFit()
                            .cornerRadius(8)
                            .frame(width: 50, height: 50)
                            .matchedGeometryEffect(id: 1 , in: animationNamespace)
                            .onTapGesture {
                                withAnimation(.easeInOut){
                                    current = (current + 1) % colors.capacity
                                }
                            }
                    }
                }
            }
        }
    }
}

这是在模拟器中: