iphone objective-c core-graphics uilabel gradient

iphone - ¿Cómo agrego un degradado al texto de un UILabel, pero no el fondo?



objective-c core-graphics (7)

El ejemplo que proporciona depende de las funciones privadas de dibujo de texto a las que no tiene acceso en el iPhone. El autor proporciona un ejemplo de cómo hacer esto usando una API pública en una publicación posterior . Su ejemplo posterior usa una imagen de gradiente para el color del texto. (Desafortunadamente, parece que su blog ha sido eliminado, pero vea la respuesta de Bach aquí por el enfoque que utilizó).

Si aún desea dibujar el degradado para el color del texto en el código, puede hacerlo subclasificando UILabel y redefiniendo -drawRect: para tener un código como el siguiente dentro de él:

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

Una deficiencia de este enfoque es que las funciones de gráficos básicos que utilizo no manejan el texto Unicode correctamente.

Lo que hace el código es voltear el contexto de dibujo verticalmente (el iPhone invierte el sistema de coordenadas de Cuarzo normal para el eje Y), establece el modo de dibujo de texto para intersecar el texto dibujado con el trazado de recorte, recorta el área para dibujar al texto , y luego dibuja un gradiente. El degradado solo llenará el texto, no el fondo.

Intenté usar el método NSString -drawAtPoint: para esto, que sí es compatible con Unicode, pero todos los caracteres se ejecutaron uno encima del otro cuando cambié el modo de texto a kCGTextClip.

hey, quiero poder rellenar con degradado el texto en un UILabel. Conozco CGGradient pero no sé cómo lo usaría en un texto de UILabel

Encontré esto en Google pero no logro hacerlo funcionar

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


Estaba buscando una solución y DotSlashSlash tiene la respuesta oculta en uno de los comentarios.

En aras de la integridad, la respuesta y la solución más simple es:

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


Esto es lo que estoy haciendo en 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 }


Podría subclasificar UILable y hacer el método de sorteo usted mismo. Ese sería probablemente el enfoque más difícil, podría haber una manera más fácil.


(Pase al final para el código fuente de la clase completa)

Respuestas muy útiles por Brad Larson y Bach. El segundo funcionó para mí, pero requiere una imagen para estar presente de antemano. Quería algo más dinámico, así que combiné ambas soluciones en una sola:

  • dibuja el degradado deseado en un UIImage
  • usa el UIImage para establecer el patrón de color

El resultado funciona y en la captura de pantalla a continuación puede ver algunos caracteres griegos también bien. (También he añadido un trazo y una sombra en la parte superior del degradado)

Aquí está el método de inicio personalizado de mi etiqueta junto con el método que representa un degradado en un UIImage (parte del código para esa funcionalidad que obtuve de una publicación de blog que no puedo encontrar ahora para hacer referencia):

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

Trataré de completar esa subclase de UILabel y publicarla.

EDITAR :

La clase está lista y está en mi repositorio de GitHub . ¡Lea sobre esto here !


SWIFT 3+

Esta solución se basa en la respuesta de @ Dimitris. Es una extensión de la clase UILabel que creará un degradado sobre el texto de la etiqueta según su pasado startColor y endColor . La extensión UILabel está a continuación:

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

Y el uso:

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

Apoyos a @Dimitris


Simplest Swift 3 Solution

Agregue una imagen a los activos de su proyecto o cree uno programáticamente y luego haga lo siguiente:

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