Skip to content

Instantly share code, notes, and snippets.

@badrinathvm
Last active June 18, 2024 20:39
Show Gist options
  • Save badrinathvm/c42ad572c27937d6d7f8d02bd6445fec to your computer and use it in GitHub Desktop.
Save badrinathvm/c42ad572c27937d6d7f8d02bd6445fec to your computer and use it in GitHub Desktop.
Gradient Animated Border
//
// ContentView.swift
// Exploration
//
//
import SwiftUI
struct ContentView: View {
var body: some View {
Card {
VStack {
Text("Hello, SwiftUI!")
.font(.title)
.foregroundColor(.black)
Text("This is a custom card view.")
.font(.subheadline)
.foregroundColor(.gray)
}
}
}
}
struct Card<Content:View>: View {
@State private var contentHeight: CGFloat = 0
var content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
VStack {
self.content()
.frame(width: UIScreen.main.bounds.width - 24, height: contentHeight + 40)
.background(
GeometryReader { geometry in
Color.clear
.onAppear {
self.contentHeight = geometry.size.height
}
}
)
.gradientBorder(
lineWidth: 2,
colors: [.red, .blue, .green, .orange],
animate: true
)
}
}
}
extension View {
func gradientBorder(cornerRadius: CGFloat = 20,
lineWidth: CGFloat,
colors: [Color],
animate: Bool) -> some View {
self.modifier(
GradientBorder(
cornerRadius: cornerRadius,
lineWidth: lineWidth,
colors: colors,
animate: animate ? .constant(true) : .constant(false)
)
)
}
}
struct GradientBorder: ViewModifier {
var cornerRadius: CGFloat
var lineWidth: CGFloat
var colors: [Color]
@State var progress: CGFloat = 0.0
@State var animationCount: Int = 0
@Binding var animate: Bool
private let animationCountLimit = 2
func body(content: Content) -> some View {
let gradient = AngularGradient(
gradient: Gradient(colors: colors),
center: .center,
startAngle: .degrees(progress),
endAngle: .degrees(progress + 360)
)
return content
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(gradient, lineWidth: lineWidth)
)
.onReceive( Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()) { _ in
if animate && animationCount < animationCountLimit {
withAnimation(Animation.linear(duration: 4.0)) {
progress = 360
} completion: {
animationCount += 1
progress = 0
}
}
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment