Gradient UIView Background in Swift

A common design element in apps is a gradient, often as the background of a view. This post walks through the implementation of a GradientBackgroundView class that:

  1. Automatically redraws when colors and drawing points are assigned
  2. Is configurable in a Storyboard and in code
  3. Automatically and correctly handles rotation and bounds changes

Override layerClass

Some examples and implementations of gradient UIView backgrounds in Swift insert a CAGradientLayer at index 0 to a view's layer. This implementation contains some drawbacks, including the need to manage the CAGradientLayer's frame during rotation and resizing.

A better approach is to override the layerClass variable, explicitly setting the base layer class to CAGradientLayer. With this implementation, UIView will instantiate layer as an instance of CAGradientLayer and will manage the layer for us, including rotation and resizing.

class GradientBackgroundView: UIView {
    // Enables more convenient access to layer
    var gradientLayer: CAGradientLayer {
        return layer as! CAGradientLayer
    }

    override open class var layerClass: AnyClass {
        return CAGradientLayer.classForCoder()
    }
}

@IBDesignable Configuration

To make GradientBackgroundView configurable we want to expose a number of configuration options. Each is an @IBInspectable variable to enable modification in a Storyboard. By using didSet to update the layer properties, the layer will automatically redraw when necessary and display correctly as a gradient in a Storyboard.

@IBDesignable class GradientBackgroundView: UIView {
    // implement cgcolorgradient in the next section
    @IBInspectable var startColor: UIColor? {
        didSet { gradientLayer.colors = cgColorGradient }
    }

    @IBInspectable var endColor: UIColor? {
        didSet { gradientLayer.colors = cgColorGradient }
    }

    @IBInspectable var startPoint: CGPoint = CGPoint(x: 0.0, y: 0.0) {
        didSet { gradientLayer.startPoint = startPoint }
    }

    @IBInspectable var endPoint: CGPoint = CGPoint(x: 1.0, y: 1.0) {
        didSet { gradientLayer.endPoint = endPoint }
    }

    // existing code
}

Updating Colors

The final step is to implement cgColorGradient, a convenience variable that contains logic needed to determine if the gradient color configuration is valid. One way to implement this is an internal var of GradientBackgroundView.

extension GradientBackgroundView {
    // For this implementation, both colors are required to display
    // a gradient. You may want to extend cgColorGradient to support
    // other use cases, like gradients with three or more colors.
    internal var cgColorGradient: [CGColor]? {
        guard let startColor = startColor, let endColor = endColor else {
            return nil
        }
        
        return [startColor.cgColor, endColor.cgColor]
    }
}