Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS UIProgressView with gradient

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?

like image 403
user3532505 Avatar asked Dec 31 '16 12:12

user3532505


2 Answers

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)
like image 141
Adrian Avatar answered Oct 13 '22 15:10

Adrian


I had the same problem and solved it by creating a gradient custom view which I then convert to an image and assign it as the progress view track image.

I then flip the progress horizontally so that the progress bar becomes the background and the track image becomes the foreground.

This has the visual effect of revealing the gradient image underneath.

You just have to remember to invert your percentages which is really simple, see example buttons and code below:

enter image description hereenter image description here

SWIFT 3 Example:

class ViewController: UIViewController {

    @IBOutlet weak var progressView: UIProgressView!   

    @IBAction func lessButton(_ sender: UIButton) {
        let percentage  = 20
        let invertedValue = Float(100 - percentage) / 100
        progressView.setProgress(invertedValue, animated: true)
    }

    @IBAction func moreButton(_ sender: UIButton) {
        let percentage  = 80
        let invertedValue = Float(100 - percentage) / 100
        progressView.setProgress(invertedValue, animated: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        //create gradient view the size of the progress view
        let gradientView = GradientView(frame: progressView.bounds)

        //convert gradient view to image , flip horizontally and assign as the track image
        progressView.trackImage = UIImage(view: gradientView).withHorizontallyFlippedOrientation()

        //invert the progress view
        progressView.transform = CGAffineTransform(scaleX: -1.0, y: -1.0)

        progressView.progressTintColor = UIColor.black
        progressView.progress = 1

    }

}

extension UIImage{
    convenience init(view: UIView) {

        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)

    }
}

@IBDesignable
class GradientView: UIView {

    private var gradientLayer = CAGradientLayer()
    private var vertical: Bool = false

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        // Drawing code

        //fill view with gradient layer
        gradientLayer.frame = self.bounds

        //style and insert layer if not already inserted
        if gradientLayer.superlayer == nil {

            gradientLayer.startPoint = CGPoint(x: 0, y: 0)
            gradientLayer.endPoint = vertical ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0)
            gradientLayer.colors = [UIColor.green.cgColor, UIColor.red.cgColor]
            gradientLayer.locations = [0.0, 1.0]

            self.layer.insertSublayer(gradientLayer, at: 0)
        }
    }

}
like image 33
George Filippakos Avatar answered Oct 13 '22 14:10

George Filippakos