Reputation: 424
I'm trying to animate opacity on appear/disappear of Text
view with a simple linear gradient.
Here's the "minimum" code I have right now:
import SwiftUI
struct ContentView: View {
@State var shown: Bool = true
var body: some View {
VStack {
ZStack {
if shown {
TextView()
}
}
.frame(height: 100)
Button("Show Toggle") {
shown.toggle()
}
}
}
}
struct TextView: View {
var body: some View {
text
.overlay(gradient)
.mask(text)
.transition(transition)
}
var gradient: LinearGradient {
LinearGradient(
colors: [Color.white, Color.blue],
startPoint: .leading, endPoint: .trailing
)
}
var transition: AnyTransition {
.asymmetric(
insertion: .opacity.animation(.linear(duration: 0.500)),
removal: .opacity.animation(.linear(duration: 0.500))
)
}
var text: some View {
Text("Hello World")
.fontWeight(.bold)
.font(.largeTitle)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Notice: I'm using the
view.overlay(gradient).mask(view)
pattern in order for the gradient view to not be greedy.
As a result, the animation looks like this:
Notice how the view doesn't "just" fade out -- it first turns black and then fades out.
I can fix opacity issues by just doing gradient.mask(text)
instead of the other pattern, but then I run into other issues with the gradient view being greedy
Upvotes: 1
Views: 398
Reputation: 73
Thought it might be helpful to note that .drawingGroup() uses Metal, which may not be necessary for your purposes in this example.
You can achieve the same effect by using .compositingGroup(), without using Metal.
text
.overlay(gradient)
.mask(text)
.compositingGroup() // here
.transition(transition)
Per Apple's documentation:
A compositing group makes compositing effects in this view’s ancestor views, such as opacity and the blend mode, take effect before this view is rendered. Use compositingGroup() to apply effects to a parent view before applying effects to this view.
Upvotes: 0
Reputation: 12125
You are drawing a black text, then overlay the gradient, then clip it with the text. Just omit the first black text, e.g.by setting its opacity to 0.
text.opacity(0)
.overlay(gradient)
.mask(text)
.transition(transition)
or use .drawingGroup
which renders the whole view offscreen before displaying:
text
.overlay(gradient)
.mask(text)
.drawingGroup() // here
.transition(transition)
Upvotes: 3