您可以推迟对视图删除过渡中使用的变量的评估吗?

问题描述 投票:0回答:1

我有一个父视图,它根据观察到的变量的值呈现几个视图之一。

在下面的简单示例中,当 @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()
swiftui swiftui-animation
1个回答
0
投票

如果在设置点击位置后异步重置标志

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)
    }
}

Animation

© www.soinside.com 2019 - 2024. All rights reserved.