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)
However, when I add it to a view, inside a VStack
for example, this happens: (click to see animation; it's a .gif)
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?
CodePudding user response:
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.
CodePudding user response:
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)
}
}