3

I made the following PulsatingView:

struct PulsatingView: View {
    
    @State private var opacity = 0.7
    @State private var scale: CGFloat = 0.5

    var body: some View {
        VStack {
            ZStack {
                Circle()
                    .fill(Color.black.opacity(opacity))
                    .animation(.easeInOut(duration: 2).delay(1.5).repeatForever(autoreverses: false))
                    .frame(width: 7, height: 7)
                    .scaleEffect(scale)
                    .animation(.easeIn(duration: 2.5).delay(1).repeatForever(autoreverses: false))
                Circle()
                    .fill(Color.black)
                    .frame(width: 7, height: 7)
            }
        }
        .onAppear {
            opacity = 0
            scale = 5
        }
    }
}

It produces the following result: (click to see animation; it's a .gif)

pulsating view screencast

However, when I add it to a view, inside a VStack for example, this happens: (click to see animation; it's a .gif)

pulsating view glitch

I have no idea why this happens or how I can fix it. I've already tried adding modifiers such as .fixedSize(horizontal: true, vertical: true) or .frame(width: 50, height: 50) to it but that doesn't help.

Does anyone know what's going on?

LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
  • Hi @LinusGeffarth, I am having this same issue at the moment. Did you find a solution? I tried the `DispatchQueue.main.async {}`, but this didn't work. Still struggling. Any help would be appreciated, thanks! – Gligor Nov 03 '21 at 09:16
  • 1
    Seems you've found a solution. For me, Asperi's answer did the job (which is why I accepted it). – LinusGeffarth Nov 03 '21 at 11:30
  • Yeah, all good, thanks for getting back to me anyways :) – Gligor Nov 04 '21 at 19:31

3 Answers3

1

Try using animation with joined value, like

Circle()
    .fill(Color.black.opacity(opacity))
    .animation(.easeInOut(duration: 2).delay(1.5).repeatForever(autoreverses: false), value: opacity) // << here !!
    .frame(width: 7, height: 7)
    .scaleEffect(scale)
    .animation(.easeIn(duration: 2.5).delay(1).repeatForever(autoreverses: false), value: scale)    // << here !!

Important: if it is started in NavigationView then animatable state should be activated postponed. The reason and investigation details was provided in https://stackoverflow.com/a/66643096/12299030.

Asperi
  • 228,894
  • 20
  • 464
  • 690
1

This issue is not about your code it is because NavigationView to fix it we should use DispatchQueue.main.async I wanted refactor your code as well, hope help you, also you can avoid hard coded value as much as possible, for example you can apply the frame in outside of view:

struct ContentView: View {
    
    var body: some View {
        
        CircleView(lineWidth: 0.5, color: .red, scale: 5.0)
            .frame(width: 7, height: 7)
        
    }
    
}




struct CircleView: View {

    let lineWidth: CGFloat
    let color: Color
    let scale: CGFloat
    
    @State private var startAnimation: Bool = Bool()
    
    var body: some View {
        
        Circle()
            .strokeBorder(style: .init(lineWidth: startAnimation ? .zero : lineWidth)).opacity(startAnimation ? .zero : 1.0)
            .scaleEffect(startAnimation ? scale : 1.0)
            .overlay( Circle() )
            .foregroundColor(color)
            .onAppear() { DispatchQueue.main.async { startAnimation.toggle() } }
            .animation(.easeIn(duration: 2.5).delay(1).repeatForever(autoreverses: false), value: startAnimation)
        
    }
}
ios coder
  • 1
  • 4
  • 31
  • 91
1

I finally managed to find a solution to this on my own project - I found the only solution to get the animated view working within a NavigationView was to move the animation inside the onAppear call, like this:

Circle()
    .fill(Color.black.opacity(opacity))
    .onAppear {
        withAnimation(.easeInOut(duration: 2).repeatForever(autoreverses: false)) {
            opacity = 0
        }
    }

Hope this can help someone else if they get stuck on the same issue I ended up having!

Gligor
  • 2,862
  • 2
  • 33
  • 40