SwiftUI:当状态改变时,带有动画的视图会跳转到目的地

时间:2021-01-19 16:20:10

标签: animation swiftui

带有 TextView 的 RectangleView 在 3 秒内滑入屏幕,但滑动时 TextView 的状态在一秒后发生了变化。现在可以看到 TextView 停止滑动,立即跳转到目的地。

我只想在滑动时将 RectangleView 与 TextView 视为一个整体,并且在状态改变时不要改变 TextView 的位置,无论它旋转或保持静止。

demo gif

import SwiftUI

struct RotationEnvironmentKey: EnvironmentKey {
    static let defaultValue: Bool = false
}

extension EnvironmentValues {
    var rotation: Bool {
        get { return self[RotationEnvironmentKey] }
        set { self[RotationEnvironmentKey] = newValue }
    }
}

struct AnimationTestView: View {
    @State private var go = false
    @State private var rotation = false
    var body: some View {
        VStack {
            Button("Go!") {
                go.toggle()
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                    rotation.toggle()
                    print("set rotation = \(rotation)")
                }
            }
            Group {
                if go {
                    RectangleView()
                        .transition(.slide)
                        .environment(\.rotation, rotation)
                }
            }.animation(.easeInOut(duration: 3.0), value: go)
        }.navigationTitle("Animation Test")
    }
}

struct RectangleView: View {
    var body: some View {
        Rectangle()
            .frame(width: 200, height: 100)
            .foregroundColor(.pink)
            .overlay(TextView())
    }
}

struct TextView: View {
    @Environment(\.rotation) var rotation
    @State private var animationRotating: Bool = false
    let animation = Animation.linear(duration: 3.0).repeatForever(autoreverses: false)
    
    var body: some View {
        print("refresh, rotation = \(rotation)"); return
        HStack {
            Spacer()
            if rotation {
                Text("R")
                    .foregroundColor(.blue)
                    .rotationEffect(.degrees(animationRotating ? 360 : 0))
                    .animation(animation, value: animationRotating)
                    .onAppear { animationRotating = true }
                    .onDisappear { animationRotating = false }
            } else {
                Text("S")
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

它因为视图被重新创建使用延迟。删除 DispatchQueue.main.asyncAfter 还使用一个单一的 bool var 进行动画。两者都不需要。

struct ContentView: View {
    @State private var go = false
    @State private var rotation = false
    var body: some View {
        VStack {
            Button("Go!") {
                go.toggle()
                rotation.toggle() //<-- Here
                
            }
            Group {
                if go {
                    RectangleView()
                        .transition(.slide)
                        .environment(\.rotation, rotation)
                }
            }.animation(.easeInOut(duration: 3.0), value: go)
        }.navigationTitle("Animation Test")
    }
}

struct RectangleView: View {
    var body: some View {
        Rectangle()
            .frame(width: 200, height: 100)
            .foregroundColor(.pink)
            .overlay(TextView())
    }
}

struct TextView: View {
    @Environment(\.rotation) var rotation
    @State private var animationRotating: Bool = false
    let animation = Animation.linear(duration: 3.0).repeatForever(autoreverses: false)
    
    var body: some View {
        print("refresh, rotation = \(rotation)"); return
        HStack {
            Spacer()
            if rotation {
                Text("R")
                    .foregroundColor(.blue)
                    .rotationEffect(.degrees(animationRotating ? 360 : 0))
                    .animation(animation.delay(1.0), value: animationRotating) //<-- here
                    .onAppear { animationRotating = true }
                    .onDisappear { animationRotating = false }
            } else {
                Text("S")
            }
        }
    }
}

编辑:

您也在更改带有动画的文本。为此,您需要更改您的正文代码。

@State private var opacity: Double = 1

var body: some View {
        print("refresh, rotation = \(rotation)"); return
            Text("R")
            .foregroundColor(.blue)
            .rotationEffect(.degrees(animationRotating ? 360 : 0))
            .animation(animation.delay(1.0), value: animationRotating) //<-- here
            .opacity(!animationRotating ? opacity : 0)
            .onAppear {
                animationRotating = true
                opacity = 1
            }
            .onDisappear {
                animationRotating = false
                opacity = 0
            }
            .overlay(Text("S")
                        .opacity(animationRotating ? opacity : 0))
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .trailing)
    }