How to Delay Animation in Swiftui

How to delay animation in SwiftUI?

From the partial code you provided it seems you are manipulating the same variables in both closures. Change that to individual ones for each field.

Reason:

when you do:

@State private var changeVar = 0.0

withAnimation(...){
changeVar = 1.0
}

SwiftUI will change that value incrementally from the value it is at the moment the animation starts to the value provided in the closure. As soon as the value changes the view gets redrawn with the current value of changeVar. As you use the same var for both Views the changes are applied to both at the same time. Hence the animation starts for both at the same time.

How to create animation with delay when dismiss view swiftui

This would be one way to do it. Without the NavigationLink you have full control over all animations and transitions.

struct DetailView: View {
@Binding var showDetail:Bool

var body: some View {
Button(
"Here is Detail View. Tap to go back.",
action: {
withAnimation(Animation.linear.delay(2)){
self.showDetail = false
}
}
).frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity).background(Color.yellow)
}
}

struct RootView: View {
@State var showDetail = false

var body: some View {
VStack {
if showDetail{
DetailView(showDetail:self.$showDetail).transition(.move(edge: .trailing))
}else{
Button("I am Root. Tap for Detail View."){
withAnimation(.linear){
self.showDetail = true
}
}
}
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity).background(Color.red)
}
}

How to trigger action after x seconds in swiftUI

Create a delay, which then sets the @State property hasTimeElapsed to true when the time has elapsed, updating the view body.

With Swift 5.5 and the new concurrency updates (async & await), you can now use task(_:) like the following:

struct ContentView: View {
@State private var hasTimeElapsed = false

var body: some View {
Text(hasTimeElapsed ? "Sorry, too late." : "Please enter above.")
.task(delayText)
}

private func delayText() async {
// Delay of 7.5 seconds (1 second = 1_000_000_000 nanoseconds)
try? await Task.sleep(nanoseconds: 7_500_000_000)
hasTimeElapsed = true
}
}

See more info about Task.sleep(nanoseconds:) in this answer.



Older versions

Xcode 13.0+ now supports concurrency, backwards compatible! However, here is still the 'old' way to do it:

You can use DispatchQueue to delay something. Trigger it with onAppear(perform:) (which happens when the view first appears). You could also hook the delay up to a Button instead if wanted.

Example:

struct ContentView: View {
@State private var hasTimeElapsed = false

var body: some View {
Text(hasTimeElapsed ? "Sorry, too late." : "Please enter above.")
.onAppear(perform: delayText)
}

private func delayText() {
// Delay of 7.5 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 7.5) {
hasTimeElapsed = true
}
}
}

Delay SwiftUI combined transitions

Try this

extension AnyTransition {
static var delayAndFade: AnyTransition {
return AnyTransition.identity
.combined(with: .opacity)
.animation(.default.delay(3))
}
}

If you want to move a view, you should animate its offset using the withAnimation function.

         Text("Move and fade.")
.offset(y: offset)
.transition(.delayAndFade)

struct ContentView: View {
@State private var showDetails = false
@State var offset:CGFloat = 0

var body: some View {
VStack {
Button("Press to show details") {
showDetails.toggle()
withAnimation(.default.delay(3)) {
self.offset = -20
}
}


if showDetails {
Text("Move and fade.")
.offset(y: offset)
.transition(.delayAndFade)
}
}
}
}

Update

extension AnyTransition {
static var moveAndFade: AnyTransition {
return AnyTransition.move(edge: .top)
.combined(with: .opacity)
}
}

Try this

HStack {
Text("Move and fade.")
}
.animation(Animation.default.delay(2))
.transition(.moveAndFade)

It works with all kind of views except Text.

struct ContentView: View {
@State private var showDetails = false
@State var offset:CGFloat = 0

var body: some View {
VStack {
Button("Press to show details") {
showDetails.toggle()

}


if showDetails {

// Works!
HStack {
Text("Move and fade.")
}
.animation(Animation.default.delay(2))
.transition(.moveAndFade)

Button("Move and fade.") {}
.animation(Animation.default.delay(2))
.transition(.moveAndFade)

// Does not work
Text("Move and fade.")
.animation(Animation.default.delay(2))
.transition(.moveAndFade)
}
}
}
}

Delay a transition in SwiftUI

If you add an explicit id it works as you would like. Note, I made only one animation delay to make it a little more obvious that this is working.

struct ContentView: View {
@State var showOne = true
var body:some View {
VStack {
if showOne {
HStack {
Spacer()
Text("One")
Spacer()
}
.background(Color.red)
.id("one")
.animation(Animation.default)
.transition(.slide)
} else {
HStack {
Spacer()
Text("Two")
Spacer()
}
.background(Color.blue)
.id("two")
.animation(Animation.default.delay(2))
.transition(.slide)
}
Button("Toggle") {
withAnimation {
self.showOne.toggle()
}
}
}
}
}

I've found an explicit id to be helpful most of the time I want to use a transition. In your example, not using the id can cause the text to change before the background. This would seem to be a bug, and I recommend filing feedback on it.

SwiftUI synchronizing multiple SwiftUI animations with delays

The fix for the out of sync views is to put them all in one view and that view syncs everything simultaneously. It took me some time to work out the pattern, but here it is:

struct MyRepeatingItemView: View {

@State var isTranslated = false

var body: some View {
ZStack {
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(isTranslated ? 0 : 0.25)
.scaleEffect(isTranslated ? 1 : 0.75)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.75 : 0.5)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.5 : 0.25)
Rectangle()
.fill(Color.red)
.frame(width: 256, height: 256)
.opacity(0.25)
.scaleEffect(isTranslated ? 0.25 : 0)
}
.onAppear {
withAnimation(Animation.linear(duration: 1).repeatForever(autoreverses: false)) {
isTranslated.toggle()
}
}
}
}

struct MyRepeatingContainerView: View {

var body: some View {
ZStack {
Color.blue
.frame(width: 256, height: 2)
Color.blue
.frame(width: 2, height: 256)

MyRepeatingItemView()
}
}
}

SwiftUI animation: How to stagger repeating animation with delay

I think you can implement it using Timer and DispatchQueue, try this and see it's working as you want or no

struct Arrows: View {
private let arrowCount = 3

let timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()

@State var scale:CGFloat = 1.0
@State var fade:Double = 0.5

var body: some View {

ZStack {
Color(red: 29.0/255.0, green: 161.0/255.0, blue: 224.0/255.0).edgesIgnoringSafeArea(.all)

HStack{
ForEach(0..<self.arrowCount) { i in
ArrowShape()
.stroke(style: StrokeStyle(lineWidth: CGFloat(10),
lineCap: .round,
lineJoin: .round ))
.foregroundColor(Color.white)
.aspectRatio(CGSize(width: 28, height: 70), contentMode: .fit)
.frame(maxWidth: 20)
.animation(nil)
.opacity(self.fade)
.scaleEffect(self.scale)
.animation(
Animation.easeOut(duration: 0.5)
//.repeatForever(autoreverses: true)
.repeatCount(1, autoreverses: true)
.delay(0.2 * Double(i))
)
}.onReceive(self.timer) { _ in
self.scale = self.scale > 1 ? 1 : 1.2
self.fade = self.fade > 0.5 ? 0.5 : 1.0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.scale = 1
self.fade = 0.5
}
}
}
}
}
}


Related Topics



Leave a reply



Submit