1

So I am trying to make some text fade in and out to give a pulsate type effect, this is the code I have now:

struct SignInView: View { @State private var opacity: Double = 0.0 @State private var pulseDown: Bool = false var body: some View { VStack(alignment: .center) { Spacer() Button(action: { AppDelegate.signInWithGoogle() }, label: { Text("Sign In") .foregroundColor(Color.green) .opacity(opacity) }) Spacer() } .padding() .onAppear { self.pulsateText() } } private func pulsateText() { DispatchQueue.init(label: "Pulse").asyncAfter(deadline: .now() + 0.01) { if self.pulseDown { self.opacity -= 0.02 } else { self.opacity += 0.02 } if self.opacity > 1 { self.pulseDown = true } else if self.opacity < 0.1 { self.pulseDown = false } self.pulsateText() } } } 

It does exactly what I want and looks good, but I can't help but feel that an infinite recursive loop is not the right way to be doing it. I suppose I could make an infinite while instead of the infinite recursion, though that still seem not ideal. Is there a better way to achieve this?

2 Answers 2

9

There is an easier more SwiftUI-like way. It works by using an Animation's repeatForever(autoreverses:) method:

struct SignInView: View { @State private var visible = true var body: some View { VStack { Spacer() Button(action: { print("Sign in with Google") // AppDelegate.signInWithGoogle() }, label: { Text("Sign In") .foregroundColor(Color.green) .opacity(visible ? 1 : 0) }) Spacer() } .padding() .onAppear(perform: pulsateText) } private func pulsateText() { withAnimation(Animation.easeInOut.repeatForever(autoreverses: true)) { visible.toggle() } } } 
Sign up to request clarification or add additional context in comments.

4 Comments

That's what I was looking for, thanks! Is there an easy way to slow it down? Edit: Figured it out, just need to pass a duration parameter to easeInOut thanks again!
It seems that after the latest Xcode update it not longer works - now the entire view slides from the top left corner to the middle of the screen and back on loop, any ideas on how to fix that?
@Quinn It still works for me, so you must have changed something since (such as adding an animation to whatever other views are in the VStack).
nope, just updated Xcode - it still works if I build it on my old computer with the exact same code - running it on my newer computer with a newer version of Xcode gives me crazy results
1

Here's a simple ViewModifier version, which isolatest the logic into a pulseEffect() similiar to the native scaleEffect():

struct PulseEffect: ViewModifier { @State private var pulseIsInMaxState: Bool = true private let range: ClosedRange<Double> private let duration: TimeInterval init(range: ClosedRange<Double>, duration: TimeInterval) { self.range = range self.duration = duration } func body(content: Content) -> some View { content .opacity(pulseIsInMaxState ? range.upperBound : range.lowerBound) .onAppear { pulseIsInMaxState = false } .animation(.smooth(duration: duration).repeatForever(), value: pulseIsInMaxState) } } public extension View { func pulseEffect(range: ClosedRange<Double> = 0.5...1, duration: TimeInterval = 1) -> some View { modifier(PulseEffect(range: range, duration: duration)) } } #Preview { Text("Hello, world!") .pulseEffect() } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.