SwiftUI inverse animation delay on removal

不问归期 提交于 2021-01-28 05:02:04

问题


I am trying to choreograph animations in SwiftUI. I want two colored bars to move in to the view with a delay between them and then move out of the view with the delays switched so that the removal is the reverse of the insertion.

I think I understand why the below code doesn't work, but I can't figure out how to make it do what I need:

import SwiftUI

struct TestAnimControl: View {
    @State var show: Bool = false
    @State var reverseDelay: Bool = false

    var body: some View {
        VStack {
            Button(action:{
                self.show.toggle()
            }) {
                Text("Animate")
                    .font(.largeTitle)
            }
            if show {
                Rectangle()
                    .foregroundColor(.blue)
                    .frame(height: 100)
                    .transition(.move(edge: .trailing))
                    .animation(Animation.spring().delay(show ? 0.3 : 0.5))
                Rectangle()
                    .foregroundColor(.red)
                    .frame(height: 100)
                    .transition(.move(edge: .trailing))
                    .animation(Animation.spring().delay(show ? 0.5 : 0.3))
            }
        }
    }
}

When you run this and hit the button, the blue bar moves in and then the red bar moves in. Hit the button again and the blue bar moves out and then the red bar moves out. What I want is when you hit the button for removal, the red bar moves out and then the blue bar moves out, reverse of the way the bars came in. In this code I believe the ternary doesn't work because the animation is set when the Rectangle is created and the delay can't change after that. I may be wrong, but either way is there a way to do what I am trying to do?


回答1:


Here is a solution. Tested with Xcode 11.4 / iOS 13.4

struct TestAnimControl: View {
    @State var show: Bool = false
    @State var reverseDelay: Bool = false

    @State var blueDelay = 0.3
    @State var redDelay = 0.5
    var body: some View {
        VStack {
            Button(action:{
                self.show.toggle()
            }) {
                Text("Animate")
                    .font(.largeTitle)
            }
            if show {
                Rectangle()
                    .foregroundColor(.blue)
                    .frame(height: 100)
                    .transition(.move(edge: .trailing))
                    .animation(Animation.spring().delay(blueDelay))//(show ? 0.3 : 0.5))
                    .onAppear { self.blueDelay = 0.5 }
                    .onDisappear { self.blueDelay = 0.3 }
                Rectangle()
                    .foregroundColor(.red)
                    .frame(height: 100)
                    .transition(.move(edge: .trailing))
                    .animation(Animation.spring().delay(redDelay))//(show ? 0.5 : 0.3))
                    .onAppear { self.redDelay = 0.3 }
                    .onDisappear { self.redDelay = 0.5 }
            }
        }
    }
}



回答2:


You cannot change delay value according to state value because Animation struct is marked as @frozen that has no effect on property observers - state properties (check it out https://docs.swift.org/swift-book/ReferenceManual/Attributes.html). The proper way to do is using DispatchQueue.main.asyncAfter(deadline:_:) to specify each delay. Here is my sample code...

struct ContentView: View {
    @State var blue = false
    @State var red = false

    var body: some View {
        VStack {
            Button(action:{
                let redDelay = self.red ? 0.3 : 0.5
                DispatchQueue.main.asyncAfter(deadline: .now() + redDelay) {
                    self.red.toggle()
                }
                let blueDelay = self.blue ? 0.5 : 0.3
                DispatchQueue.main.asyncAfter(deadline: .now() + blueDelay) {
                    self.blue.toggle()
                }
            }) {
                Text("Animate")
                    .font(.largeTitle)
            }
            if blue {
                Rectangle()
                    .foregroundColor(.blue)
                    .frame(height: 100)
                    .transition(AnyTransition.move(edge: .trailing))
                    .animation(Animation.spring())
            }
            if red {
                Rectangle()
                    .foregroundColor(.red)
                    .frame(height: 100)
                    .transition(AnyTransition.move(edge: .trailing))
                    .animation(Animation.spring())
            }
        }
        .animation(.spring())
    }
}

Thanks. X_X



来源:https://stackoverflow.com/questions/62141416/swiftui-inverse-animation-delay-on-removal

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!