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

Posted

技术标签:

【中文标题】如何为 UILabel 的文本添加渐变,而不是背景?【英文标题】:How do I add a gradient to the text of a UILabel, but not the background? 【发布时间】:2010-11-18 23:12:01 【问题描述】:

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

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

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

【问题讨论】:

这似乎是这个问题的重复:***.com/questions/422066/… 实际上这个问题是问如何在 uilabel 的背景中添加渐变,而不是向文本本身添加渐变 对不起,我看错了你的问题。请参阅下文,了解为文本设置渐变的方法,而不是背景。 看到这篇文章***.com/q/7002874/495180 【参考方案1】:

我一直在寻找解决方案,而 DotSlashSlash 的答案隐藏在其中一个 cmets 中!

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

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

【讨论】:

这很好用!比核心图形解决方案干净得多! 很好的答案。不过要小心谨慎地使用colorWithPatternImage,因为它可能会占用大量内存。 是的,这需要一些时间来实现,我同意勾选这个! @Bach 如果在尺寸适合的 UIlabel 上使用,是否可以相应地调整图案图像的大小? @luca 图像不会被缩放,只会在 x 和 y 中重复。因此,如果您使用渐变,请确保图像高度与标签的高度完全相同,然后您可以拥有灵活的宽度。【参考方案2】:

(跳到底部查看完整课程源代码)

Brad Larson 和 Bach 的回答非常有用。第二个对我有用,但它需要预先存在图像。我想要更动态的东西,所以我将两种解决方案合二为一:

在 UIImage 上绘制所需的渐变 使用 UIImage 设置颜色模式

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

这是我的标签的自定义 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;

我会尝试完成那个 UILabel 子类并发布它。

编辑

课程结束,在my GitHub repository。阅读它here!

【讨论】:

这是唯一适用于多行文本的答案,谢谢! 有机会更新到 Swift 4 吗?【参考方案3】:

斯威夫特 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)
    

用法:

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

【讨论】:

水平渐变效果极佳【参考方案4】:

SWIFT 3+

此解决方案基于@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
    


及用法:

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


SWIFT 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;

@Dimitris 的道具

【讨论】:

当我把它放到 ImageView 时不起作用,整个视图是黄色的,里面没有文字...... 你是对的@J.Doe。我已经用 Swift 3+ 的解决方案更新了我的答案。此外,我还包含了我自己的转折和UILabel 的扩展,它将渐变放置在UIColor 中并将其应用于标签本身。如果还有其他问题,请告诉我。 我正是需要这个:) 谢谢你。现在是 2018 年,渐变文本颜色仍然很难实现。 史诗,我怎样才能使渐变水平呢?【参考方案5】:

您提供的示例依赖于您在 iPhone 上无法访问的私有文本绘图功能。 作者在subsequent post 中提供了一个如何使用公共API 执行此操作的示例。他后面的示例使用渐变图像作为文本颜色。 (不幸的是,他的博客似乎已被删除,但请参阅 Bach's answer here 了解他使用的方法。)

如果您仍想在代码中为您的文本颜色绘制渐变,可以通过继承 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);

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

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

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

【讨论】:

谢谢 :) 我只是简单地使用了图案图像方法 label.textColor = [UIColor colorWithPatternImage:gradientImage; @PsychoDad - 不幸的是,看起来他的整个博客已被删除,但其中的关键信息已在 Bach 的答案中捕获,并且此处的其余答案仍然有效。 Dimitris 甚至在他的回答中对此进行了扩展。感谢您指出。【参考方案6】:

这就是我在 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

【讨论】:

【参考方案7】:

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

我们只需两步即可实现:

    创建渐变图像 将渐变图像作为文本颜色应用到 UILabel

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!
    

如下使用:

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

2.将渐变图像作为文本颜色应用到UILabel

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

注意:

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

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

注2:

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

【讨论】:

【参考方案8】:

最简单的 Swift 3 解决方案

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

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

【讨论】:

不知道为什么这被否决了!虽然不是程序化的,但效果很好 + 极少。【参考方案9】:
yourLabel.textColor = UIColor(patternImage: UIImage(named: "ur gradient image name ")!)

【讨论】:

不知道为什么这也被否决了!!同样,虽然不是程序化的,但效果很好 + 最少。【参考方案10】:

SwiftUI

虽然我们在 SwiftUI 中使用 Text 而不是 UILabel,但如果您考虑如何在 Text 上应用渐变,您应该将其应用为 mask。但由于渐变是可拉伸的,您可以像这样进行简单的扩展:

extension View 
    func selfSizeMask<T: View>(_ mask: T) -> some View 
        ZStack 
            self.opacity(0)
            mask.mask(self)
        .fixedSize()
    


演示

然后您可以将任何渐变或其他类型的视图分配为自大小的蒙版,例如:

Text("Gradient is on FIRE !!!")
    .selfSizeMask(
        LinearGradient(
            gradient: Gradient(colors: [.red, .yellow]),
            startPoint: .bottom,
            endPoint: .top)
    )

此方法包含一些额外优势,您可以在此处查看 in this answer

【讨论】:

【参考方案11】:

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

【讨论】:

是的,我已经做到了,但是渐变确实填充了整个视图的文本 我的意思是它没有填充文本

以上是关于如何为 UILabel 的文本添加渐变,而不是背景?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 UILabel 设置黑色透明? [复制]

如何为我在 Android 中用作背景的可绘制对象添加圆角?

在 UITableViewCell 中添加渐变隐藏 UILabel 文本

如何为 UIButton 设置自定义渐变背景?

如何为 iOS 应用程序的 UILabel 设置左上对齐?

如何为图案填充添加背景颜色?