我有一个父视图,它根据观察到的变量的值呈现几个视图之一。
在下面的简单示例中,当 @Published 变量“settings.showRainbow”为 true 时,将呈现 RainbowView。
当用户点击彩虹的一种颜色时,RainbowView 将“settings.showRainbow”设置为 false,这会导致父视图将其删除,并显示另一个视图。点击位置也是由 RainbowView 设置的。
当RainbowView被移除时,它的移除过渡动画是围绕用户点击的位置进行放大。
它工作得很好......当然,除了移除过渡的 .scale 锚点的值(变量“tapLocation”)是在呈现视图时设置的,而不是在移除视图时设置的。
我想要的是 SwiftUI 等待捕获锚点位置,直到视图即将被删除的时间点。
有人知道这是否可能吗?我可以让子视图在即将被关闭时负责为自己设置动画(效果很好),但我更希望让父视图负责删除动画。
这是一个代码片段:
if settings.showRainbow {
RainbowView()
.transition(AnyTransition
.asymmetric(
insertion: .opacity,
removal: .scale(scale: 24,
anchor: tapLocation)))
} else {
OtherView()
如果在设置点击位置后异步重置标志
showRainbow
,似乎可以工作。
我发现有必要在微小的延迟后执行重置:0.01s并不总是可靠的,0.05s效果更好。
struct ContentView: View {
@State private var showRainbow = false
@State private var tapLocation = UnitPoint.zero
struct OtherView: View {
var body: some View {
Text("OtherView")
}
}
struct RainbowView: View {
let rainbowColors: [Color] = [.red, .orange, .yellow, .green, .blue, .indigo, .purple]
var body: some View {
ZStack {
ForEach(Array(rainbowColors.enumerated()), id: \.offset) { offset, color in
Circle()
.trim(from: 0.5, to: 1.0)
.stroke(color, lineWidth: 10)
.padding(CGFloat(offset) * 10)
}
}
}
}
var body: some View {
ZStack {
// if settings.showRainbow {
if showRainbow {
RainbowView()
.frame(width: 250)
.transition(
.asymmetric(
insertion: .opacity,
removal: .scale(scale: 24, anchor: tapLocation)
)
)
.onTapGesture { location in
tapLocation = UnitPoint(x: location.x / 250, y: location.y / 250)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
showRainbow = false
}
}
} else {
OtherView()
.onTapGesture { showRainbow = true }
}
}
.animation(.easeInOut(duration: 1), value: showRainbow)
}
}