Skip to content

Instantly share code, notes, and snippets.

@kieranb662
Last active August 26, 2024 23:01
Show Gist options
  • 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

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.

@tyler-waite
Copy link

I updated the script to use iOS 17's withAnimation method. I also integrated @Viogenius and @akatzfey1's changes.

//
//  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 {
    @State private var isOn: Bool = false
    let hueColors = stride(from: 0, to: 1, by: 0.01).map {
        Color(hue: $0, saturation: 1, brightness: 1)
    }
    
    var duration: Double = 4
    
    func body(content: Content) -> some View {
        let gradient = LinearGradient(
            gradient: Gradient(colors: hueColors + hueColors),
            startPoint: .leading,
            endPoint: .trailing
        )
        
        return content
            .overlay(GeometryReader { proxy in
                ZStack {
                    gradient
                        .frame(width: 2 * proxy.size.width)
                        .offset(x: self.isOn ? -proxy.size.width : 0)
                }
            })
            .onAppear {
                withAnimation(.linear(duration: self.duration).repeatForever(autoreverses: false)) {
                    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()
    }
}

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