我正在尝试为 LazyVGrid 视图之间的移动设置动画。我有两个小圆圈形状(即“点”)数组,排列在两个不同的 LazyVGrid 视图中(一个在左侧,一个在右侧)。当用户点击按钮时,我希望对每个点进行动画处理,使其从原始位置滑入第三个 LazyVGrid 中的位置,并将所有这些点组合起来。
就好像你有自主但听话的弹珠,在桌子上的两个地方排列成网格,当你命令它们这样做时,它们滚动在一起,在桌子中央形成一个新的网格(但更简单——没有碰撞)等)。
经过多次尝试,下面的代码本质上是我迄今为止最好的尝试(尽管脱离上下文看起来很愚蠢)。 “填充”按钮填充原始点,但它仍然只是动画,就像原始点网格淡出和新点淡入,然后随着第一组新点向上滑动,更多新点淡入。
关于实施此类行为有什么建议吗?
import SwiftUI
struct CT2: View {
@StateObject private var viewModel = DotManager()
var body: some View {
VStack {
ZStack {
HStack {
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
ForEach(viewModel.blueDots) { dot in
dot.dot
.frame(width: 20, height: 20)
.foregroundColor(.blue)
}
})
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
ForEach(viewModel.redDots) { dot in
dot.dot
.frame(width: 20, height: 20)
.foregroundColor(.red)
}
})
}
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 20), count: 5), content: {
ForEach(viewModel.combinedDots) { dot in
dot.dot
.frame(width: 20, height: 20)
.foregroundColor(.purple)
}
})
}
}
HStack {
Button(action: {
viewModel.addDots()
}, label: {
Text("Fill")
})
.padding()
.foregroundColor(.primary)
.background(Color(UIColor.systemFill))
Button(action: {
viewModel.combineDots()
}, label: {
Text("Combine dots")
})
.padding()
.foregroundColor(.primary)
.background(Color(UIColor.systemFill))
}
}
}
enum DotType {
case blue, red, combined
}
class Dot: Identifiable {
let id = UUID()
let dot = Circle()
}
class DotManager: ObservableObject {
@Published var blueDots: [Dot] = []
@Published var redDots: [Dot] = []
@Published var combinedDots: [Dot] = []
func addDots() {
for _ in 0..<17 {
let newDot = Dot()
blueDots.append(newDot)
}
for _ in 17..<28 {
let newDot = Dot()
redDots.append(newDot)
}
}
func combineDots() {
for _ in 0..<blueDots.count {
withAnimation(.easeInOut) {
if let movedDot = blueDots.first {
blueDots.removeFirst()
combinedDots.append(movedDot)
}
}
}
for _ in 0..<redDots.count {
withAnimation(.easeInOut) {
if let movedDot = redDots.first {
redDots.removeFirst()
combinedDots.append(movedDot)
}
}
}
}
}
要使视图达到所需的效果,您可以使用 @Namespace 和 .matchedGeometryEffect
修改后的代码如下。
struct CT2: View {
@StateObject private var viewModel = DotManager()
@Namespace private var namespace
... same ...
}
并在每个 ForEach 循环内(这里有三个 ForEach),添加 .matchedGeometryEffect。
ForEach(viewModel.blueDots) { dot in
dot.dot
.frame(width: 20, height: 20)
.foregroundColor(.blue)
.matchedGeometryEffect(id: dot.id, in: namespace)
}