如何使用 matchedGeometryEffect 在 SwiftUI 中转换子视图?
How to transition subviews in SwiftUI using matchedGeometryEffect?
我有许多 SwapItem
结构,每个结构都有一个 child SwapItemChild
。然后,使用 SwiftUI 的 ForEach
,我想显示每个 SwapItem
的名称,称为项目视图,还包含一个圆圈,其颜色分别为 SwapItemChild
,称为child 查看。随后,我想交换两个项目的 children,并让相应的 child 视图更改位置动画。这受到了 this extensive tutorial 的这种效果的其他示例的启发,但并非特别是 children 视图交换。
我尝试使用 matchedGeometryEffect
通过各自 SwapItemChild
的 ID 识别每个 child 视图来做到这一点。但是,这会导致动画跳动,只有顶部 child 视图向下移动,而底部 child 视图会立即跳到顶部。
功能示例代码如下
// 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)
}
}
}
要使这些 child 视图无缝交换位置,matchedGeometryEffect
的正确实现是什么?
我已经遇到过这种问题,试试这个:
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
我有许多 SwapItem
结构,每个结构都有一个 child SwapItemChild
。然后,使用 SwiftUI 的 ForEach
,我想显示每个 SwapItem
的名称,称为项目视图,还包含一个圆圈,其颜色分别为 SwapItemChild
,称为child 查看。随后,我想交换两个项目的 children,并让相应的 child 视图更改位置动画。这受到了 this extensive tutorial 的这种效果的其他示例的启发,但并非特别是 children 视图交换。
我尝试使用 matchedGeometryEffect
通过各自 SwapItemChild
的 ID 识别每个 child 视图来做到这一点。但是,这会导致动画跳动,只有顶部 child 视图向下移动,而底部 child 视图会立即跳到顶部。
功能示例代码如下
// 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)
}
}
}
要使这些 child 视图无缝交换位置,matchedGeometryEffect
的正确实现是什么?
我已经遇到过这种问题,试试这个:
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