-
-
Save kieranb662/40e1faeffe3490d3a04910ec24f9298e to your computer and use it in GitHub Desktop.
// | |
// 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() | |
} | |
} |
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)
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.
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.
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()
}
}
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