【问题标题】:How to transition subviews in SwiftUI using matchedGeometryEffect?如何使用matchedGeometryEffect在SwiftUI中转换子视图?
【发布时间】:2020-10-13 13:31:15
【问题描述】:

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

【问题讨论】:

  • 这不是这个工具的工作方式。 matchedGeometryEffect不同 层次结构中具有相同 ID 的两个视图之间工作。参见例如stackoverflow.com/a/64414233/12299030
  • 我没有从文档中解释该限制。 Apple:“如果在同一事务中插入一个视图,而另一个具有相同键的视图被删除,系统将在窗口空间中插入它们的框架矩形,以使其看起来有一个视图从其旧位置移动到新位置."。我认为我的方法是在新位置插入子视图并从旧位置删除它,因此应该可以与matchedGeometryEffect 一起使用。

标签: view swiftui transition


【解决方案1】:

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

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

【讨论】:

  • 谢谢!我是否正确理解这种方式,列表中的行由 SwiftUI(用于动画等)由 both SwapItemSwapItemChild 标识?这将不同于我最初的实现,它们仅由SwapItem 标识。您的动画是否正常工作,因为基本上两行都被“替换”,即没有行保留相同的标识符,而不是像我的实现中那样在交换后保留相同的标识符?有谁知道为什么这会产生正确的帧插值?
猜你喜欢
  • 1970-01-01
  • 2020-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-19
  • 2020-08-11
相关资源
最近更新 更多