UILabel with color gradient to certain height of t

2019-06-04 23:26发布

I want to achieve the effect from the picture bellow - a UILabel that shows progress X as a gradient from bottom to X% of height of the label. The remaining (100 - X)% of the label will have a different color.

desired effect

The only thing that comes into my mind right now is to create two UIViews one with gray background and one with the color of the gradient. Put the gradient view above the gray view and set it’s height to match the current progress. Then simply use the label as a mask for those two views. For better illustration I attached a picture describing my suggested solution. Though I’m not happy with it because it is not very elegant.

suggested solution



Is it possible to achieve this in a different and more elegant way? Ideally just by subclassing UILabel.

3条回答
男人必须洒脱
2楼-- · 2019-06-05 00:02

You can subclass UILabel and draw what you want in draw(_ rect: CGRect) function. Or if you want to do it fast you can subclass too and add gradient subview. Don't forget to resize it in layoutSubviews() function.

查看更多
祖国的老花朵
3楼-- · 2019-06-05 00:14

You could do this with layers and masks, but it will actually be easier to just set the text color with a UIColor from pattern image. This code works, though it might be better to subclass UILabel and give the class a method to apply and or update the image. I say this is easier, because I find dealing with text layers a bit of a pain to get the sizing perfect, where as labels can alter their font size with adjustsFontSizeToFitWidth.

 override func viewDidLayoutSubviews() {
    label.textColor = UIColor(patternImage: partialGradient(forViewSize: label.frame.size, proportion: 0.65))
}

func partialGradient(forViewSize size: CGSize, proportion p: CGFloat) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(size, false, 0)

    let context = UIGraphicsGetCurrentContext()


    context?.setFillColor(UIColor.darkGray.cgColor)
    context?.fill(CGRect(origin: .zero, size: size))

    let c1 = UIColor.orange.cgColor
    let c2 = UIColor.red.cgColor

    let top = CGPoint(x: 0, y: size.height * (1.0 - p))
    let bottom = CGPoint(x: 0, y: size.height)

    let colorspace = CGColorSpaceCreateDeviceRGB()

    if let gradient = CGGradient(colorsSpace: colorspace, colors: [c1, c2] as CFArray, locations: [0.0, 1.0]){
        // change 0.0 above to 1-p if you want the top of the gradient orange
        context?.drawLinearGradient(gradient, start: top, end: bottom, options: CGGradientDrawingOptions.drawsAfterEndLocation)
    }


    let img = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return img!
}

enter image description here

查看更多
男人必须洒脱
4楼-- · 2019-06-05 00:24

You can use Core Animation with CATextLayer and CAGradientLayer.

import PlaygroundSupport

let bgView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
bgView.backgroundColor = UIColor.black
PlaygroundPage.current.liveView = bgView

let textLayer = CATextLayer()
textLayer.frame = bgView.frame
textLayer.string = "70"
textLayer.fontSize = 60

let gradientLayer = CAGradientLayer()
gradientLayer.frame = bgView.frame
gradientLayer.colors = [
    UIColor.gray.cgColor,
    UIColor(red: 1, green: 122.0/255.0, blue: 0, alpha: 1).cgColor,
    UIColor(red: 249.0/255.0, green: 1, blue: 0, alpha: 1).cgColor
]

//Here you can adjust the filling
gradientLayer.locations = [0.5, 0.51, 1]

gradientLayer.mask = textLayer
bgView.layer.addSublayer(gradientLayer)

enter image description here

查看更多
登录 后发表回答