Skip to content

Instantly share code, notes, and snippets.

@kieranb662
Last active May 25, 2023 12:58
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kieranb662/40e1faeffe3490d3a04910ec24f9298e to your computer and use it in GitHub Desktop.
Save kieranb662/40e1faeffe3490d3a04910ec24f9298e to your computer and use it in GitHub Desktop.
[Rainbow ViewModifier] Rainbow animation view modifiers for SwiftUI #SwiftUI #ViewModifier
//
// Rainbow.swift
// Created by Kieran Brown on 4/15/20.
import SwiftUI
struct Rainbow: ViewModifier {
let hueColors = stride(from: 0, to: 1, by: 0.1).map {
Color(hue: $0, saturation: 1, brightness: 1)
}
func body(content: Content) -> some View {
content
.overlay(GeometryReader { (proxy: GeometryProxy) in
ZStack {
LinearGradient(gradient: Gradient(colors: self.hueColors),
startPoint: .leading,
endPoint: .trailing)
.frame(width: proxy.size.width, height: proxy.size.height)
}
})
.mask(content)
}
}
extension View {
func rainbow() -> some View {
self.modifier(Rainbow())
}
}
struct RainbowAnimation: ViewModifier {
// 1
@State var isOn: Bool = false
let hueColors = stride(from: 0, to: 1, by: 0.01).map {
Color(hue: $0, saturation: 1, brightness: 1)
}
// 2
var duration: Double = 4
var animation: Animation {
Animation
.linear(duration: duration)
.repeatForever(autoreverses: false)
}
func body(content: Content) -> some View {
// 3
let gradient = LinearGradient(gradient: Gradient(colors: hueColors+hueColors), startPoint: .leading, endPoint: .trailing)
return content.overlay(GeometryReader { proxy in
ZStack {
gradient
// 4
.frame(width: 2*proxy.size.width)
// 5
.offset(x: self.isOn ? -proxy.size.width/2 : proxy.size.width/2)
}
})
// 6
.onAppear {
withAnimation(self.animation) {
self.isOn = true
}
}
.mask(content)
}
}
extension View {
func rainbowAnimation() -> some View {
self.modifier(RainbowAnimation())
}
}
struct RainbowExamples: View {
var body: some View {
ZStack {
Color(white: 0.1).edgesIgnoringSafeArea(.all)
VStack {
Capsule()
.frame(width: 200, height: 75)
.rainbow()
RoundedRectangle(cornerRadius: 10)
.inset(by: 5)
.stroke(Color.black, lineWidth: 5)
.frame(width: 300, height: 100)
.rainbowAnimation()
Text("Rainbow")
.font(.system(size: 100))
.rainbowAnimation()
}
}
}
}
struct Rainbow_Previews: PreviewProvider {
static var previews: some View {
RainbowExamples()
}
}
@Viogenius
Copy link

Viogenius commented Oct 30, 2020

Thanks, really useful. I a trying to edit it so it doesn't display the original text color at the begining. I am running it on X Code12 and simulators and the text is black then the animation comes from the rigth

@Viogenius
Copy link

Viogenius commented Oct 30, 2020

Got it, just changed this line 👍

.offset(x: self.isOn ? -proxy.size.width/2 : proxy.size.width/2)
to
.offset(x: self.isOn ? -proxy.size.width/2 : 0)

@Viogenius
Copy link

Viogenius commented Oct 30, 2020

And if you change it to :

.offset(x: self.isOn ? -proxy.size.width : 0)

it looks much nicer the animation is flowing normally and the colors don't come back at the middle.

@akatzfey1
Copy link

I'm running this on XCode 13.1 and found some issues when attaching the .rainbow() modifier to a view nested inside of a NavigationView. The modified view slides in from the top left corner along with displaying the rainbow effect. To fix this I changed the following bit of code:

        .onAppear {
            withAnimation(self.animation) {
                self.isOn = true
            }
        }

To the following:

        .onAppear {
            DispatchQueue.main.async {
                withAnimation(self.animation) {
                    self.isOn = true
                }
            }
        }

Not sure why extra animation is added to the parent view but wrapping withAnimation with async seems to fix the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment