LinearGradient in SwiftUI

DevTechie Inc
Jun 17, 2022


Photo by Luke Chesser on Unsplash

Linear gradient applies the color function along an axis, as defined by its start and end points. The gradient maps the unit space points into the bounding rectangle of each shape filled with the gradient.

LinearGradient is a view in SwiftUI which is used to draw a linear gradient. This view conforms to ShapeStyle protocol.

Let’s start with a simple example. We will create an array for colors we want to use in our gradient view and pass it while initializing the LinearGradient as shown below:

struct LinearGradientExample: View {
    
    let colors: [Color] = [.purple, .blue, .cyan, .teal]
    
    var body: some View {
        LinearGradient(colors: colors, startPoint: .top, endPoint: .bottom)
            .ignoresSafeArea()
    }
}
We can easily animate colors in LinearGradient by adding animation modifier and by changing start and end points, as shown below:

struct LinearGradientExample: View {
    
    let colors: [Color] = [.purple, .blue, .cyan, .teal]
    let availablePoints = [UnitPoint.top, .topLeading, .topTrailing, .bottom, .bottomLeading, .bottomTrailing, .center, .leading, .trailing]
    
    @State private var currentPoints = [UnitPoint.top, .bottom]
    var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        LinearGradient(colors: colors, startPoint: currentPoints[0], endPoint: currentPoints[1])
            .animation(.easeInOut(duration: 2), value: currentPoints)
            .onReceive(timer, perform: { _ in
                currentPoints = [availablePoints.randomElement()!, availablePoints.randomElement()!]
            })
            .ignoresSafeArea()
    }
}
UnitPoint can be defined in terms of x, y values as well so let’s use that to animate start and end points for our gradient:

struct LinearGradientExample: View {
    
    let colors: [Color] = [.purple, .blue, .cyan, .teal]
    
    @State private var currentPoints = [UnitPoint.top, .bottom]
    var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        LinearGradient(colors: colors, startPoint: currentPoints[0], endPoint: currentPoints[1])
            .animation(.easeInOut(duration: 2), value: currentPoints)
            .onReceive(timer, perform: { _ in
                currentPoints = [UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1)), UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1))]
            })
            .ignoresSafeArea()
    }
}
Other overload for LinearGradient initializer takes Gradient as a parameter.

struct LinearGradientExample: View {
    
    let colors: [Color] = [.purple, .blue, .cyan, .teal]
    
    @State private var currentPoints = [UnitPoint.top, .bottom]
    var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        LinearGradient(gradient: Gradient(colors: colors), startPoint: currentPoints[0], endPoint: currentPoints[1])
            .animation(.easeInOut(duration: 2), value: currentPoints)
            .onReceive(timer, perform: { _ in
                currentPoints = [UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1)), UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1))]
            })
            .ignoresSafeArea()
    }
}
There is one more overload for LinearGradient which takes Gradient.Stop array as a parameter. Gradient.stop is made of color and location for that color in the gradient. Let’s animate that too 😃

struct LinearGradientExample: View {
    
    let stops: [Gradient.Stop] =
    [Gradient.Stop(color: .purple, location: CGFloat.random(in: 0.0...0.2)),
     Gradient.Stop(color: .blue, location: CGFloat.random(in: 0.2...0.4)),
     Gradient.Stop(color: .cyan, location: CGFloat.random(in: 0.4...0.6)),
     Gradient.Stop(color: .teal, location: CGFloat.random(in: 0.6...1.0))]
    
    @State private var currentPoints = [UnitPoint.top, .bottom]
    var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        LinearGradient(stops: stops, startPoint: currentPoints[0], endPoint: currentPoints[1])
            .animation(.easeInOut(duration: 2), value: currentPoints)
            .onReceive(timer, perform: { _ in
                currentPoints = [UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1)), UnitPoint(x: CGFloat.random(in: 0...1), y: CGFloat.random(in: 0...1))]
            })
            .ignoresSafeArea()
    }
}
Note the values of stop locations. They are in order from 0.0–0.2, 0.2–0.4..,0.6–1.0. Its because stop locations have to be in order.

With that we have reached the end of this article. Thank you once again for reading. Subscribe our weekly newsletter at