objective-c 如何为 UILabel 的文本而不是背景添加渐变?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1266179/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 22:03:52  来源:igfitidea点击:

How do I add a gradient to the text of a UILabel, but not the background?

iphoneobjective-ccore-graphicsuilabelgradient

提问by DotSlashSlash

hey, I want to be able to have a gradient fill on the text in a UILabel I know about CGGradient but i dont know how i would use it on a UILabel's text

嘿,我希望能够对 UILabel 中的文本进行渐变填充,我知道 CGGradient,但我不知道如何在 UILabel 的文本上使用它

i found this on google but i cant manage to get it to work

我在谷歌上找到了这个,但我无法让它工作

http://silverity.livejournal.com/26436.html

http://silverity.livejournal.com/26436.html

回答by Bach

I was looking for a solution and DotSlashSlash has the answer hidden in one of the comments!

我一直在寻找解决方案,DotSlashSlash 在其中一条评论中隐藏了答案!

For the sake of completeness, the answer and the simplest solution is:

为了完整起见,答案和最简单的解决方案是:

UIImage *myGradient = [UIImage imageNamed:@"textGradient.png"];
myLabel.textColor   = [UIColor colorWithPatternImage:myGradient];

回答by Dimitris

(Skip to bottom for full class source code)

(跳到底部获取完整的类源代码)

Really useful answers by both Brad Larson and Bach. The second worked for me but it requires an image to be present in advance. I wanted something more dynamic so I combined both solutions into one:

Brad Larson 和 Bach 的真正有用的答案。第二个对我有用,但它需要提前提供图像。我想要更动态的东西,所以我将两种解决方案合二为一:

  • draw the desired gradient on a UIImage
  • use the UIImage to set the color pattern
  • 在 UIImage 上绘制所需的渐变
  • 使用 UIImage 设置颜色模式

The result works and in the screenshot below you can see some Greek characters rendered fine too. (I have also added a stroke and a shadow on top of the gradient)

结果有效,在下面的屏幕截图中,您可以看到一些希腊字符也渲染得很好。(我还在渐变顶部添加了笔触和阴影)

iOS stylized UILabel, the big brown fox

iOS 风格化 UILabel,棕色大狐狸

Here's the custom init method of my label along with the method that renders a gradient on a UIImage (part of the code for that functionality I got from a blog post I can not find now to reference it):

这是我的标签的自定义 init 方法以及在 UIImage 上呈现渐变的方法(我从博客文章中获得的该功能的一部分代码,我现在找不到引用它):

- (id)initWithFrame:(CGRect)frame text:(NSString *)aText {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        self.text = aText;

        self.textColor = [UIColor colorWithPatternImage:[self gradientImage]];

    }
    return self;
}

- (UIImage *)gradientImage
{
    CGSize textSize = [self.text sizeWithFont:self.font];
    CGFloat width = textSize.width;         // max 1024 due to Core Graphics limitations
    CGFloat height = textSize.height;       // max 1024 due to Core Graphics limitations

    // create a new bitmap image context
    UIGraphicsBeginImageContext(CGSizeMake(width, height));

    // get context
    CGContextRef context = UIGraphicsGetCurrentContext();       

    // push context to make it current (need to do this manually because we are not drawing in a UIView)
    UIGraphicsPushContext(context);                             

    //draw gradient    
    CGGradientRef glossGradient;
    CGColorSpaceRef rgbColorspace;
    size_t num_locations = 2;
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 0.0, 1.0, 1.0, 1.0,  // Start color
                            1.0, 1.0, 0.0, 1.0 }; // End color
    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    CGPoint topCenter = CGPointMake(0, 0);
    CGPoint bottomCenter = CGPointMake(0, textSize.height);
    CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, 0);

    CGGradientRelease(glossGradient);
    CGColorSpaceRelease(rgbColorspace); 

    // pop context 
    UIGraphicsPopContext();                             

    // get a UIImage from the image context
    UIImage *gradientImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up drawing environment
    UIGraphicsEndImageContext();

    return  gradientImage;
}

I'll try to complete that UILabel subclass and post it.

我将尝试完成 UILabel 子类并发布它。

EDIT:

编辑

The class is done and it's on my GitHub repository. Read about it here!

这门课已经完成,它在我的 GitHub 存储库上在这里阅读!

回答by Brandon A

SWIFT 3+

斯威夫特 3+

This solution is based on @Dimitris's answer. It is an extension on the UILabelclass that will create a gradient over the label's text per your passed startColorand endColor. The UILabelextension is below:

此解决方案基于@Dimitris 的回答。它是UILabel该类的扩展,它将根据您传递的startColorendColor. 该UILabel扩展是以下:

extension UILabel {

    func applyGradientWith(startColor: UIColor, endColor: UIColor) -> Bool {

        var startColorRed:CGFloat = 0
        var startColorGreen:CGFloat = 0
        var startColorBlue:CGFloat = 0
        var startAlpha:CGFloat = 0

        if !startColor.getRed(&startColorRed, green: &startColorGreen, blue: &startColorBlue, alpha: &startAlpha) {
            return false
        }

        var endColorRed:CGFloat = 0
        var endColorGreen:CGFloat = 0
        var endColorBlue:CGFloat = 0
        var endAlpha:CGFloat = 0

        if !endColor.getRed(&endColorRed, green: &endColorGreen, blue: &endColorBlue, alpha: &endAlpha) {
            return false
        }

        let gradientText = self.text ?? ""

        let name:String = NSFontAttributeName
        let textSize: CGSize = gradientText.size(attributes: [name:self.font])
        let width:CGFloat = textSize.width
        let height:CGFloat = textSize.height

        UIGraphicsBeginImageContext(CGSize(width: width, height: height))

        guard let context = UIGraphicsGetCurrentContext() else {
            UIGraphicsEndImageContext()
            return false
        }

        UIGraphicsPushContext(context)

        let glossGradient:CGGradient?
        let rgbColorspace:CGColorSpace?
        let num_locations:size_t = 2
        let locations:[CGFloat] = [ 0.0, 1.0 ]
        let components:[CGFloat] = [startColorRed, startColorGreen, startColorBlue, startAlpha, endColorRed, endColorGreen, endColorBlue, endAlpha]
        rgbColorspace = CGColorSpaceCreateDeviceRGB()
        glossGradient = CGGradient(colorSpace: rgbColorspace!, colorComponents: components, locations: locations, count: num_locations)
        let topCenter = CGPoint.zero
        let bottomCenter = CGPoint(x: 0, y: textSize.height)
        context.drawLinearGradient(glossGradient!, start: topCenter, end: bottomCenter, options: CGGradientDrawingOptions.drawsBeforeStartLocation)

        UIGraphicsPopContext()

        guard let gradientImage = UIGraphicsGetImageFromCurrentImageContext() else {
            UIGraphicsEndImageContext()
            return false
        }

        UIGraphicsEndImageContext()

        self.textColor = UIColor(patternImage: gradientImage)

        return true
    }

}

And usage:

和用法:

let text = "YAAASSSSS!"
label.text = text
if label.applyGradientWith(startColor: .red, endColor: .blue) {
    print("Gradient applied!")
}
else {
    print("Could not apply gradient")
    label.textColor = .black
}

YAAASSSSS!

哎呀!



SWIFT 2

快速 2

class func getGradientForText(text: NSString) -> UIImage {

    let font:UIFont = UIFont(name: "YourFontName", size: 50.0)!
    let name:String = NSFontAttributeName
    let textSize: CGSize = text.sizeWithAttributes([name:font])
    let width:CGFloat = textSize.width         // max 1024 due to Core Graphics limitations
    let height:CGFloat = textSize.height       // max 1024 due to Core Graphics limitations

    //create a new bitmap image context
    UIGraphicsBeginImageContext(CGSizeMake(width, height))

    // get context
    let context = UIGraphicsGetCurrentContext()

    // push context to make it current (need to do this manually because we are not drawing in a UIView)
    UIGraphicsPushContext(context!)

    //draw gradient
    let glossGradient:CGGradientRef?
    let rgbColorspace:CGColorSpaceRef?
    let num_locations:size_t = 2
    let locations:[CGFloat] = [ 0.0, 1.0 ]
    let components:[CGFloat] = [(202 / 255.0), (197 / 255.0), (52 / 255.0), 1.0,  // Start color
                                (253 / 255.0), (248 / 255.0), (101 / 255.0), 1.0] // End color
    rgbColorspace = CGColorSpaceCreateDeviceRGB();
    glossGradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);
    let topCenter = CGPointMake(0, 0);
    let bottomCenter = CGPointMake(0, textSize.height);
    CGContextDrawLinearGradient(context, glossGradient, topCenter, bottomCenter, CGGradientDrawingOptions.DrawsBeforeStartLocation);

    // pop context
    UIGraphicsPopContext();

    // get a UIImage from the image context
    let gradientImage = UIGraphicsGetImageFromCurrentImageContext();

    // clean up drawing environment
    UIGraphicsEndImageContext();

    return  gradientImage;
}

Props to @Dimitris

@Dimitris 的道具

回答by Brad Larson

The example you provide relies on private text drawing functions that you don't have access to on the iPhone. The author provides an example of how to do this using public API in a subsequent post.His later example uses a gradient image for the color of the text. (Unfortunately, it appears his blog has since been removed, but see Bach's answer herefor the approach he used.)

您提供的示例依赖于您无法在 iPhone 上访问的私有文本绘制功能。 作者在后续博文中提供了如何使用公共 API 执行此操作的示例。他后面的例子使用渐变图像作为文本的颜色。(不幸的是,他的博客似乎已被删除,但请在此处查看巴赫对他使用的方法的回答。)

If you still want to draw the gradient for your text color in code, it can be done by subclassing UILabel and overriding -drawRect: to have code like the following within it:

如果你仍然想在代码中为你的文本颜色绘制渐变,可以通过继承 UILabel 并覆盖 -drawRect: 来完成,在其中包含如下代码:

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0.0f, self.bounds.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);

CGContextSelectFont(context, "Helvetica", 20.0f, kCGEncodingMacRoman);
CGContextSetTextDrawingMode(context, kCGTextClip);
CGContextSetTextPosition(context, 0.0f, round(20.0f / 4.0f));
CGContextShowText(context, [self.text UTF8String], strlen([self.text UTF8String]));

CGContextClip(context);

CGGradientRef gradient;
CGColorSpaceRef rgbColorspace;
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 1.0, 1.0, 1.0,  // Start color
    1.0, 1.0, 1.0, 0.1 }; // End color

rgbColorspace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(rgbColorspace, components, locations, num_locations);

CGRect currentBounds = self.bounds;
CGPoint topCenter = CGPointMake(CGRectGetMidX(currentBounds), 0.0f);
CGPoint midCenter = CGPointMake(CGRectGetMidX(currentBounds), CGRectGetMidY(currentBounds));
CGContextDrawLinearGradient(context, gradient, topCenter, midCenter, 0);

CGGradientRelease(gradient);
CGColorSpaceRelease(rgbColorspace);         

CGContextRestoreGState(context);

One shortcoming of this approach is that the Core Graphics functions I use don't handle Unicode text properly.

这种方法的一个缺点是我使用的 Core Graphics 函数不能正确处理 Unicode 文本。

What the code does is it flips the drawing context vertically (the iPhone inverts the normal Quartz coordinate system on for the Y axis), sets the text drawing mode to intersect the drawn text with the clipping path, clips the area to draw to the text, and then draws a gradient. The gradient will only fill the text, not the background.

代码的作用是垂直翻转绘图上下文(iPhone 反转 Y 轴的正常 Quartz 坐标系),设置文本绘图模式以将绘制的文本与剪切路径相交,剪切区域以绘制文本,然后绘制渐变。渐变只会填充文本,而不是背景。

I tried using NSString's -drawAtPoint: method for this, which does support Unicode, but all the characters ran on top of one another when I switched the text mode to kCGTextClip.

我尝试为此使用 NSString 的 -drawAtPoint: 方法,该方法确实支持 Unicode,但是当我将文本模式切换到 kCGTextClip 时,所有字符都在彼此之上。

回答by Binh Le

Swift 4.1

斯威夫特 4.1

class GradientLabel: UILabel {
    var gradientColors: [CGColor] = []

    override func drawText(in rect: CGRect) {
        if let gradientColor = drawGradientColor(in: rect, colors: gradientColors) {
            self.textColor = gradientColor
        }
        super.drawText(in: rect)
    }

    private func drawGradientColor(in rect: CGRect, colors: [CGColor]) -> UIColor? {
        let currentContext = UIGraphicsGetCurrentContext()
        currentContext?.saveGState()
        defer { currentContext?.restoreGState() }

        let size = rect.size
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
                                        colors: colors as CFArray,
                                        locations: nil) else { return nil }

        let context = UIGraphicsGetCurrentContext()
        context?.drawLinearGradient(gradient,
                                    start: CGPoint.zero,
                                    end: CGPoint(x: size.width, y: 0),
                                    options: [])
        let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        guard let image = gradientImage else { return nil }
        return UIColor(patternImage: image)
    }
}

Usage:

用法:

label.gradientColors = [UIColor.blue.cgColor, UIColor.red.cgColor]

回答by CodyMace

Here's what I'm doing in Swift 3

这是我在 Swift 3 中所做的

override func viewDidLoad() {
    super.viewDidLoad()
    timerLabel.textColor = UIColor(patternImage: gradientImage(size: timerLabel.frame.size, color1: CIColor(color: UIColor.green), color2: CIColor(color: UIColor.red), direction: .Left))
}

func gradientImage(size: CGSize, color1: CIColor, color2: CIColor, direction: GradientDirection = .Up) -> UIImage {

    let context = CIContext(options: nil)
    let filter = CIFilter(name: "CILinearGradient")
    var startVector: CIVector
    var endVector: CIVector

    filter!.setDefaults()

    switch direction {
    case .Up:
        startVector = CIVector(x: size.width * 0.5, y: 0)
        endVector = CIVector(x: size.width * 0.5, y: size.height)
    case .Left:
        startVector = CIVector(x: size.width, y: size.height * 0.5)
        endVector = CIVector(x: 0, y: size.height * 0.5)
    case .UpLeft:
        startVector = CIVector(x: size.width, y: 0)
        endVector = CIVector(x: 0, y: size.height)
    case .UpRight:
        startVector = CIVector(x: 0, y: 0)
        endVector = CIVector(x: size.width, y: size.height)
    }

    filter!.setValue(startVector, forKey: "inputPoint0")
    filter!.setValue(endVector, forKey: "inputPoint1")
    filter!.setValue(color1, forKey: "inputColor0")
    filter!.setValue(color2, forKey: "inputColor1")

    let image = UIImage(cgImage: context.createCGImage(filter!.outputImage!, from: CGRect(x: 0, y: 0, width: size.width, height: size.height))!)
    return image
}

回答by Yodagama

yourLabel.textColor = UIColor(patternImage: UIImage(named: "ur gradient image name ")!)

回答by Eray Alparslan

There is a really simple solution for this! Here's how you add gradient colors to UILabel text.

有一个非常简单的解决方案!以下是向 UILabel 文本添加渐变颜色的方法。

We will achieve this in just two steps:

我们将通过两个步骤实现这一目标:

  1. Create Gradient Image
  2. Apply Gradient Image As textColor to UILabel
  1. 创建渐变图像
  2. 将渐变图像作为 textColor 应用于 UILabel

1.Create Gradient Image

1.创建渐变图像

extension UIImage {
    static func gradientImageWithBounds(bounds: CGRect, colors: [CGColor]) -> UIImage {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = colors

        UIGraphicsBeginImageContext(gradientLayer.bounds.size)
        gradientLayer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image!
    }
}

Use this as follows:

使用方法如下:

let gradientImage = UIImage.gradientImageWithBounds(bounds: myLabel.bounds, colors: [firstColor.cgColor, secondColor.cgColor])

?

?

2.Apply Gradient Image As textColor to UILabel

2.将渐变图像作为 textColor 应用到 UILabel

myLabel.textColor = UIColor.init(patternImage: gradientImage)

?

?

Note:

笔记:

If you want the gradient to be horizontal, just add these two lines to gradientLayer instance:

如果你希望渐变是水平的,只需将这两行添加到gradientLayer实例中:

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

?

?

Note 2:

笔记2:

The UIImage extension function works with other UIViews too; not just UILabel! So feel free to use this method no matter which UIView you use to apply gradient color.

UIImage 扩展功能也适用于其他 UIView;不仅仅是 UILabel!因此,无论您使用哪种 UIView 来应用渐变色,都可以随意使用此方法。

回答by Ryan Detzel

You could sub-class out UILable and do the draw method yourself. That would probably be the more difficult approach, there might be an easier way.

您可以对 UILable 进行子类化并自己执行 draw 方法。那可能是更困难的方法,可能有更简单的方法。

回答by Ben Sullivan

Simplest Swift 3 Solution

最简单的 Swift 3 解决方案

Add an image to your project assets or create one programmatically then do the following:

将图像添加到您的项目资产或以编程方式创建一个,然后执行以下操作:

let image = UIImage(named: "myGradient.png")!
label.textColor = UIColor.init(patternImage: image)