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

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


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

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

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


(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:

  • draw the desired gradient on a UIImage
  • use the UIImage to set the color pattern

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

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

- (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.

EDIT:

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


SWIFT 3+

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

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 = textif label.applyGradientWith(startColor: .red, endColor: .blue) {    print("Gradient applied!")}else {    print("Could not apply gradient")    label.textColor = .black}

YAAASSSSS!


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

Props to @Dimitris