How to Use Gradients in SwiftUI - Linear Gradients

How to Use Gradients in SwiftUI - Linear Gradients
💡
This is the first post in the series of Gradients in SwiftUI. If you want to learn about radial gradients in SwiftUI, please follow Radial Gradients in SwiftUI link to know more about it

Apple defines general gradient as

A color gradient represented as an array of color stops, each having a parametric location value.

The color gradient is nothing but the collection of colors organized by a pre-defined rule. You can have a color gradient with as many colors as you want. Unless you specify stop points and locations, all colors will be equally distributed. However, the gradient range needs to be customizable and Apple gives a way to specify how far you want to distribute each color.

Technically, Gradient is a base structure and all other specialized structures are based on it. SwiftUI offers 3 types of built-in gradients as follows,

  1. LinearGradient
  2. RadialGradient
  3. AngularGradient

Let's take a look at the LinearGradient in today's blog post. I will cover the rest of the gradients in future blog posts.

LinearGradient

As noted from Apple's documentation,

The 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.

Linear gradient takes the list of colors as input and applies them in a linear fashion. There are 3 types of initialized LinearGradient offers as follows,


/// Creates a linear gradient from a base gradient.
public init(gradient: Gradient, startPoint: UnitPoint, endPoint: UnitPoint)

/// Creates a linear gradient from a collection of colors.
public init(colors: [Color], startPoint: UnitPoint, endPoint: UnitPoint)

/// Creates a linear gradient from a collection of color stops.
public init(stops: [Gradient.Stop], startPoint: UnitPoint, endPoint: UnitPoint)

Although each initializer has its own signature, the concept remains the same. With the first and third APIs, you get more flexibility for controlling how much space each color takes and its start and end position.

The second API takes the list of colors as input and distributes them equally. Unlike the other two APIs, this API does not provide flexibility in terms of how these colors are distributed in the given space.

💡
Since there is a lot to talk about this API, I am going to divide this post into two parts. The first part will talk about how to each of these APIs. The second part will go into more detail on how to get more control to be able to create any kind of linear gradient with this API

Creating a Linear Gradient from  a Base Gradient

In its simplest form, this gradient initializer allows us to first create an instance of Gradient and pass it to create a LinearGradient. An instance of Gradient can be created by passing an array of stops where each stop represents the color and the stop.

With this, we can create a gradient of any design by adding variation to colors and their respective stops.

Just like the LinearGradient, Gradient can also be created by passing the list of colors. Let's take a look at examples,

💡
For all the following examples, to keep things simpler, I am using the default gradient direction from top to bottom. I will expand more on how to customize directions in the next section

Creating a LinearGradient with Gradient instance where Gradient is created from the list of colors.


let gradient = Gradient(colors: [
    Color.blue,
    Color.pink,
    Color.yellow
])

Rectangle()
    .fill(LinearGradient(gradient: gradient, startPoint: .top, endPoint: .bottom))
    .frame(width: 200, height :100)

Creating a LinearGradient with Gradient instance where Gradient is created from the list of color stops.


let gradientThreeColors = Gradient(stops: [
    .init(color: Color.blue, location: 0.2),
    .init(color: Color.pink, location: 0.4),
    .init(color: Color.yellow, location: 0.8)
]
)

Rectangle()
    .fill(LinearGradient(gradient: gradientThreeColors, startPoint: .top, endPoint: .bottom))
    .frame(width: 200, height: 100)

Creating a Linear Gradient from  a Collection of Colors

The creation of LinearGradient is not limited to the creation of Gradient instance. LinearGradient can also be instantiated by simply passing the collection of colors. All the colors are equally distributed in the gradient so the downside is, that you don't get the flexibility to distribute them in an unequal manner.


let colors = [
    Color.blue,
    Color.pink,
    Color.yellow
]

Rectangle()
    .fill(LinearGradient(colors: colors, startPoint: .top, endPoint: .bottom))
    .frame(width: 200, height :100)

Creating a Linear Gradient from a Collection of Color Stops

The final way to create a LinearGradient is from the collection of color stops. You create an array of color stops where each stop represents the color and its location and pass that list to LinearGradient initializer to create a gradient as directed by color stops.


let colorStops: [Gradient.Stop] = [
    .init(color: Color.blue, location: 0.2),
    .init(color: Color.pink, location: 0.4),
    .init(color: Color.yellow, location: 0.8)
]

Rectangle()
    .fill(LinearGradient(stops: colorStops, startPoint: .top, endPoint: .bottom))
    .frame(width: 200, height :100)

Understanding Gradient Directions

Now that we learned different ways to create a linear gradient, let's see how to control gradient directions using startPoint and endPoint. If we consider all the initializers of LinearGradient, all of them take two other input parameters startPoint and endPoint. These points specify where the gradient is going to start and end.

Let's demonstrate it by using a simple example of creating a linear gradient containing three colors with top as a startPoint and bottom as an endPoint.


let colors = Gradient(colors: [
    Color.blue,
    Color.pink,
    Color.yellow
])

Rectangle()
    .fill(LinearGradient(stops: colors, startPoint: .top, endPoint: .bottom))
    .frame(width: 200, height :100)

Because it was going from top to bottom, the first color blue starts at the top and the gradient ends with the last color yellow.

We can easily toggle it to go from leading to trailing or even diagonally from topLeading edge to bottomTrailing and the other way around.

💡
startPoint and endPoint values we pass above are of type UnitPoint. UnitPoint is represented by x and y coordinates. For example, the top leading point in a given space is defined by UnitPoint backed by (x: 0, y: 0) coordinates, and the bottom trailing point is represented by UnitPoint back by (x: 1, y: 1). iOS has added convenient static variables to this enum which lets developers specify the point directions without having to initialize them with (x, y) coordinates. For example, we can use UnitPoint.topLeading to represent the top leading direction without having to pass UnitPoint instance explicitly like UnitPoint(x: 0, y: 0).

In all, UnitPoint provides the following 10 built-in direction points that developers can use without worrying about UnitPoint's x and y values.

  1. top
  2. bottom
  3. leading
  4. trailing
  5. topLeading (Same as zero)
  6. topTrailing
  7. bottomLeading
  8. bottomTrailing
  9. zero
  10. center

Summary

So this was all about Linear Gradients on iOS and how to use them in SwiftUI. Hope this post was useful to understand how linear gradients are formed and how to use them in iOS applications. Since this post became too long, I decided to break down the next couple of gradient topics - Radial Gradients and Angular Gradients in separate blog posts. I will soon be working on them and will be available in upcoming blog posts.

💡
Update: The blog post on radial gradients is now ready. Please follow How to Create Radial Gradients in SwiftUI to know more about rendering and using radial gradients in SwiftUI.