首页 > 解决方案 > 如何使用matchedGeometryEffect在SwiftUI中转换子视图?

问题描述

我有许多SwapItem结构,每个都有一个 child SwapItemChild。然后,使用ForEachSwiftUI,我想显示每个的名称SwapItem,称为项目视图,还包含一个与其各自颜色的圆圈SwapItemChild,称为子视图。随后,我想交换两个项目的孩子,并让各自的孩子视图改变位置动画。这是受此广泛教程的其他示例的启发,但并非专门针对儿童视图交换。

matchedGeometryEffect我尝试通过各自的 id 来识别每个子视图来做到这一点SwapItemChild。然而,这会导致一个跳跃的动画,只有顶部的子视图向下移动,而底部的子视图会瞬间跳到顶部。

功能示例代码如下。

// 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让这些子视图无缝交换位置的正确实现是什么?

标签: viewswiftuitransition

解决方案


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

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

交换动画

另一种方式 :

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


推荐阅读