iosswiftgradientuibezierpathcagradientlayer

Create a line graph with gradients in iOS


What's the best way to create a graph like this in iOS?

enter image description here

My first thought was to create a bezier path and then add a gradient layer with the different locations. But this can't work since the documentation specifies that:

The values must be monotonically increasing.

Which is not the case in my graph.

Any thoughts on good ways to achieve this?

Thanks


Solution

  • You can do this by using a CAGradientLayer as the background of your chart, and then a CAShapeLayer as a mask of the gradient layer. The mask layer will only show the layer beneath in areas that it is drawn on.

    This playground code gives a general idea, using randomly generated data:

    import UIKit
    import PlaygroundSupport
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
    view.backgroundColor = .black
    
    // Gradient for the chart colors
    let gradient = CAGradientLayer()
    gradient.colors = [
        UIColor.red.cgColor,
        UIColor.orange.cgColor,
        UIColor.yellow.cgColor,
        UIColor.green.cgColor
    ]
    gradient.startPoint = CGPoint(x: 0.5, y: 0)
    gradient.endPoint = CGPoint(x: 0.5, y: 1)
    
    gradient.frame = view.bounds
    view.layer.addSublayer(gradient)
    
    // Random points
    let graph = CAShapeLayer()
    let path = CGMutablePath()
    var y: CGFloat = 150
    let points: [CGPoint] = stride(from: CGFloat.zero, to: 300, by: 2).map {
        let change = CGFloat.random(in: -20...20)
        var newY = y + change
        newY = max(10, newY)
        newY = min(newY, 300)
        y = newY
        return CGPoint(x: $0, y: y)
    }
    path.addLines(between: points)
    graph.path = path
    graph.fillColor = nil
    graph.strokeColor = UIColor.black.cgColor
    graph.lineWidth = 4
    graph.lineJoin = .round
    graph.frame = view.bounds
    
    // Only show the gradient where the line is
    gradient.mask = graph
    
    PlaygroundPage.current.liveView = view
    

    Results:

    enter image description here