Animated Gradient Background
A linear gradient that slowly drifts its colors and turns its direction.

- Backgrounds
- iOS 18.0+
- beginner
- Origin
- Original work
- Found by
- background · gradient · linear gradient · animated · colors · drift · fondo · degradado · gradiente · animado · colores · movimiento
Description
Section titled “Description”A linear gradient whose colors drift along it while its direction slowly turns.
With Reduce Motion on, it shows a still gradient.
struct AnimatedGradientBackgroundDemo: View { var body: some View { ZStack { AnimatedGradientBackground()
VStack(spacing: 6) { Text("Good Evening") .font(.system(.largeTitle, design: .rounded, weight: .bold)) Text("Colors that never sit still") .font(.headline) .foregroundStyle(.white.opacity(0.8)) } .foregroundStyle(.white) } }}API reference
Section titled “API reference”| Parameter | Type | Default | Description |
|---|---|---|---|
colors | [Color] | [.indigo, .purple, .pink, .orange] | |
speed | Double | 0.2 | How many color steps pass each second. |
Copy this code into your project, or download the file.
// SPDX-License-Identifier: MIT// Copyright (c) 2026 Oscar Canton and SwiftUI Blocks contributors
import SwiftUI
enum AnimatedGradientBackgroundMath { /// The palette index for a position, also when a negative speed runs time backwards. static func index(at position: Double, count: Int) -> Int { (Int(floor(position)) % count + count) % count }}
/// A linear gradient whose colors drift along it while its direction slowly turns.////// With Reduce Motion on, it shows a still gradient.struct AnimatedGradientBackground: View { var colors: [Color] = [.indigo, .purple, .pink, .orange] /// How many color steps pass each second. var speed: Double = 0.2
@Environment(\.accessibilityReduceMotion) private var reduceMotion
var body: some View { TimelineView(.animation(minimumInterval: 1.0 / 30.0, paused: reduceMotion)) { timeline in let time = reduceMotion ? 0 : timeline.date.timeIntervalSinceReferenceDate * speed let angle = time * 0.5 let reach = CGSize(width: cos(angle) * 0.75, height: sin(angle) * 0.75)
LinearGradient( colors: driftedColors(at: time), startPoint: UnitPoint(x: 0.5 - reach.width, y: 0.5 - reach.height), endPoint: UnitPoint(x: 0.5 + reach.width, y: 0.5 + reach.height) ) } .ignoresSafeArea() .accessibilityHidden(true) }
/// Each stop blends from one palette color to the next, wrapping around so the drift never ends. private func driftedColors(at time: Double) -> [Color] { guard colors.count > 1 else { return colors + colors }
return colors.indices.map { index in let position = time + Double(index) let lower = AnimatedGradientBackgroundMath.index(at: position, count: colors.count) let upper = (lower + 1) % colors.count return colors[lower].mix(with: colors[upper], by: position - floor(position)) } }}
// MARK: - Demo
struct AnimatedGradientBackgroundDemo: View { var body: some View { ZStack { AnimatedGradientBackground()
VStack(spacing: 6) { Text("Good Evening") .font(.system(.largeTitle, design: .rounded, weight: .bold)) Text("Colors that never sit still") .font(.headline) .foregroundStyle(.white.opacity(0.8)) } .foregroundStyle(.white) } }}
#Preview { AnimatedGradientBackgroundDemo()}