iOS带有渐变效果的UIProgressView

13

是否可以创建一个自定义的UI进度视图,并带有从左到右的渐变?

我尝试使用以下代码:

    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

但是,不幸的是,梯度不可见。还有其他想法吗?


https://www.cocoacontrols.com/controls/amgprogressview - PlusInfosys
你将它作为子视图添加到你的视图中了吗? - Munib
我在故事板中添加了它,并在init函数中调用上述代码。 - user3532505
3个回答

20
查看 UIProgressView 文档,有这个属性: progressImage

如果提供了自定义图像,则忽略 progressTintColor 属性。

考虑到这一点,最懒的方法是创建渐变图像并将其设置为 progressImage
我改编了这个扩展,使其更加简洁、可伸缩和安全。
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`
    }
}

现在我们有了一种动态创建渐变图像的方法,下面是如何使用它的步骤:
let gradientImage = UIImage.gradientImage(with: progressView.frame,
                                        colors: [UIColor.red.cgColor, UIColor.green.cgColor],
                                        locations: nil)

然后,您只需设置progressViewprogressImage,如下所示:

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

3
请确保progressView.frame有宽度和高度,否则在gradientLayer.render(in: UIGraphicsGetCurrentContext()!)处会导致崩溃。 - Anna Harrison

8

我曾经遇到同样的问题,通过创建一个渐变自定义视图,将其转换为图像并将其分配为进度视图轨道图像来解决了这个问题。

然后我水平翻转进度条,使进度条成为背景,轨道图像成为前景。

这具有显示渐变图像的视觉效果。

您只需要记住反转百分比,这非常简单,请参见以下示例按钮和代码:

enter image description hereenter image description here

SWIFT 3示例:

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)
        }
    }

}

1

George 想出了一个非常聪明的方法。如果你想要更简单的解决方案,打开 UIProgressView 文档,有一个名为 progressImage 的属性。

所以,我只需要像这样使它工作:

progressView.progressImage = UIImage(named: "your_gradient_progress_icon")
progressView.trackTintColor = UIColor.clear

之后:

progressView.setProgress(currentProgress, animated: true)

George解决了一个独特的问题,你可能忽略了 - 渐变(或任何图像)将被压缩以适应进度条内部,无论进度条有多大。他试图只显示渐变/图像的一部分,并且随着接近100%,会逐渐显示更多的内容。 - s g

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接