如何在SwiftUI中使用matchedGeometryEffect过渡子视图?

时间:2020-10-13 13:31:15

标签: view swiftui transition

我有多个SwapItem结构,每个结构都有一个子SwapItemChild。然后,使用SwiftUI的ForEach,我要显示每个SwapItem的名称,称为项目视图,其中还包含一个颜色分别为SwapItemChild的圆,称为儿童观。随后,我想交换两个项目的子项,并让各自的子视图更改动画位置。受this extensive tutorial影响的其他示例的启发,但并非专门针对子级视图交换。

我尝试使用matchedGeometryEffect通过相应的SwapItemChild的ID来标识每个子视图的方法。但是,这会导致跳动的动画,其中只有顶部子视图向下移动,而底部子视图会立即跳到顶部。

功能示例代码如下。

// MARK: - Model
struct SwapItem: Identifiable {
    let id = UUID()
    let name: String
    var child: SwapItemChild
}

struct SwapItemChild: Identifiable {
    let id = UUID()
    let color: Color
}

class SwapItemStore: ObservableObject {
    @Published private(set) var items = [SwapItem(name: "Task 1", child: SwapItemChild(color: .red)),
                                         SwapItem(name: "Task 2", child: SwapItemChild(color: .orange))]
    
    func swapOuterChildren(){
        let tmpChild = items[0].child
        items[0].child = items[1].child
        items[1].child = tmpChild
    }
}

// MARK: - View
struct SwapTestView: View {
    @StateObject private var swapItemStore = SwapItemStore()
    @Namespace private var SwapViewNS
    
    var body: some View {
        VStack(spacing: 50.0) {
            Button(action: swapItemStore.swapOuterChildren){
                Text("Swap outer children")
                    .font(.title)
            }
            
            VStack(spacing: 150.0) {
                ForEach(swapItemStore.items){ item in
                    SwapTestItemView(item: item, ns: SwapViewNS)
                }
            }
        }
    }
}

struct SwapTestItemView: View {
    let item: SwapItem
    let ns: Namespace.ID
    
    var body: some View {
        HStack {
            Circle()
                .fill(item.child.color)
                .frame(width: 100, height: 100)
                .matchedGeometryEffect(id: item.child.id, in: ns)
                .animation(.spring())
            
            Text(item.name)
        }
    }
}

matchedGeometryEffect使这些子视图无缝交换位置的正确实现是什么?

1 个答案:

答案 0 :(得分:0)

我已经遇到过这种问题,请尝试:

ForEach(swapItemStore.items, id: \.self.child.id)

swap animation

另一种方式:

struct SwapItem: Identifiable, Hashable {
    let id = UUID()
    let name: String
    var child: SwapItemChild
}

struct SwapItemChild: Identifiable, Hashable {
    let id = UUID()
    let color: Color
}

与:

ForEach(swapItemStore.items, id: \.self)

请参阅:https://www.hackingwithswift.com/books/ios-swiftui/why-does-self-work-for-foreach