iOS UIProgressView with gradient

user3532505 picture user3532505 · Dec 31, 2016 · Viewed 7.7k times · Source

is it possible to create a custom ui progress view with a gradient from left to right?

I've tried it with the following code:

    let gradientLayer = CAGradientLayer()
    gradientLayer.frame = self.frame

    gradientLayer.anchorPoint = CGPoint(x: 0, y: 0)
    gradientLayer.position = CGPoint(x: 0, y: 0)

    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0);
    gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0);

    gradientLayer.colors = [
        UIColor.red,
        UIColor.green
    ]

    // Convert to UIImage
    self.layer.insertSublayer(gradientLayer, at: 0)
    self.progressTintColor = UIColor.clear
    self.trackTintColor = UIColor.black

But unfortunately the gradient is not visible. Any other ideas?

Answer

Adrian picture Adrian · Nov 3, 2018

Looking at UIProgressView documentation, there's this property:

progressImage

If you provide a custom image, the progressTintColor property is ignored.

With that in mind, the laziest way to do this would be to create your gradient image and set it as the progressImage

I adapted this extension to make it a little cleaner, scaleable, and safer.

fileprivate extension UIImage {
    static func gradientImage(with bounds: CGRect,
                            colors: [CGColor],
                            locations: [NSNumber]?) -> UIImage? {

        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = colors
        // This makes it horizontal
        gradientLayer.startPoint = CGPoint(x: 0.0,
                                        y: 0.5)
        gradientLayer.endPoint = CGPoint(x: 1.0,
                                        y: 0.5)

        UIGraphicsBeginImageContext(gradientLayer.bounds.size)
        gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()
        return image`
    }
}

Now that we've got a way to create a gradient image "on the fly", here's how to use it:

let gradientImage = UIImage.gradientImage(with: progressView.frame,
                                        colors: [UIColor.red.cgColor, UIColor.green.cgColor],
                                        locations: nil)

From there, you'd just set your progressView's progressImage, like so:

// I'm lazy...don't force unwrap this
progressView.progressImage = gradientImage!
progressView.setProgress(0.75, animated: true)