带有渐变的 iOS UIProgressView

Posted

技术标签:

【中文标题】带有渐变的 iOS UIProgressView【英文标题】:iOS UIProgressView with gradient 【发布时间】:2016-12-31 12:19:03 【问题描述】:

是否可以创建一个从左到右渐变的自定义 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

但不幸的是渐变是不可见的。还有其他想法吗?

【问题讨论】:

cocoacontrols.com/controls/amgprogressview 您是否将其作为子视图添加到您的视图中? 我在storyboard里面添加了,在init函数中调用了上面的代码。 【参考方案1】:

UIProgressView documentation,有这个属性:

progressImage

如果您提供自定义图像,progressTintColor 属性将被忽略。

考虑到这一点,最懒惰的方法是创建渐变图像并将其设置为progressImage

我调整了 this extension 使其更清洁、可扩展和更安全。

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)

【讨论】:

你需要确保progressView.frame有一个宽度和高度,否则它会在gradientLayer.render(in: UIGraphicsGetCurrentContext()!)上崩溃【参考方案2】:

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

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

这具有露出下方渐变图像的视觉效果。

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

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)
        
    


【讨论】:

【参考方案3】:

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

所以,我只是让它像这样工作:

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

之后:

progressView.setProgress(currentProgress, animated: true)

【讨论】:

以上是关于带有渐变的 iOS UIProgressView的主要内容,如果未能解决你的问题,请参考以下文章

带有 uiscrollview 分页的 iOS 渐变动画

带有 MapKit ios 的渐变折线

CSS:带有渐变“叠加”的背景图像

iOS渐变填充文字动画

使用带有径向渐变的 CAGradientLayer 时出现奇怪的渐变。

正确删除带有渐变的图层