Using a repeating animation on a view has weird behaviour when used inside if statements.
If you want to do:
if something {
BlinkingView()
}
use a transition with an animation modifier, otherwise the view stays on the screen even after something
is set to false.
I made this extension to show a view that repeats change from one state to the next and back:
extension AnyTransition {
static func repeating<T: ViewModifier>(from: T, to: T, duration: Double = 1) -> AnyTransition {
.asymmetric(
insertion: AnyTransition
.modifier(active: from, identity: to)
.animation(Animation.easeInOut(duration: duration).repeatForever())
.combined(with: .opacity),
removal: .opacity
)
}
}
This makes the view appear and disappear with AnyTransition.opacity
and while it is shown it switches between the from
and to
state with a delay of duration
.
Example usage:
struct Opacity: ViewModifier {
private let opacity: Double
init(_ opacity: Double) {
self.opacity = opacity
}
func body(content: Content) -> some View {
content.opacity(opacity)
}
}
struct ContentView: View {
@State var showBlinkingView: Bool = false
var body: some View {
VStack {
if showBlinkingView {
Text("I am blinking")
.transition(.repeating(from: Opacity(0.3), to: Opacity(0.7)))
}
Spacer()
Button(action: {
self.showBlinkingView.toggle()
}, label: {
Text("Toggle blinking view")
})
}.padding(.vertical, 50)
}
}
Edit:
When the show condition is true on appear, the transition doesn't start. To fix this I do toggle the condition on appear of the superview (The VStack in my example):
.onAppear {
if self.showBlinkingView {
self.showBlinkingView.toggle()
DispatchQueue.main.async {
self.showBlinkingView.toggle()
}
}
}