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