framework español ios objective-c iphone cocoa-touch core-graphics

ios - español - core graphics swift



¿Cómo hago para que UILabel muestre el texto delineado? (11)

Todo lo que quiero es un borde negro de un píxel alrededor de mi texto UILabel blanco.

Llegué a subclasificar a UILabel con el siguiente código, el cual formé torpemente a partir de algunos ejemplos en línea tangencialmente relacionados. Y funciona, pero es muy, pero muy lento (excepto en el simulador) y tampoco pude hacer que centrara el texto verticalmente (así que codifiqué el valor y en la última línea temporalmente). Ahhhh!

void ShowStringCentered(CGContextRef gc, float x, float y, const char *str) { CGContextSetTextDrawingMode(gc, kCGTextInvisible); CGContextShowTextAtPoint(gc, 0, 0, str, strlen(str)); CGPoint pt = CGContextGetTextPosition(gc); CGContextSetTextDrawingMode(gc, kCGTextFillStroke); CGContextShowTextAtPoint(gc, x - pt.x / 2, y, str, strlen(str)); } - (void)drawRect:(CGRect)rect{ CGContextRef theContext = UIGraphicsGetCurrentContext(); CGRect viewBounds = self.bounds; CGContextTranslateCTM(theContext, 0, viewBounds.size.height); CGContextScaleCTM(theContext, 1, -1); CGContextSelectFont (theContext, "Helvetica", viewBounds.size.height, kCGEncodingMacRoman); CGContextSetRGBFillColor (theContext, 1, 1, 1, 1); CGContextSetRGBStrokeColor (theContext, 0, 0, 0, 1); CGContextSetLineWidth(theContext, 1.0); ShowStringCentered(theContext, rect.size.width / 2.0, 12, [[self text] cStringUsingEncoding:NSASCIIStringEncoding]); }

Solo tengo la sensación de que estoy pasando por alto una forma más simple de hacer esto. Tal vez anulando "drawTextInRect", pero parece que no puedo hacer que drawTextInRect se doble a mi voluntad a pesar de mirarlo atentamente y fruncir el ceño realmente duro.


¿Por qué no creas una vista UIView de 1px en Photoshop, luego configuras una UIView con la imagen y la colocas detrás de tu UILabel?

Código:

UIView *myView; UIImage *imageName = [UIImage imageNamed:@"1pxBorderImage.png"]; UIColor *tempColour = [[UIColor alloc] initWithPatternImage:imageName]; myView.backgroundColor = tempColour; [tempColour release];

Te va a ahorrar la subclasificación de un objeto y es bastante simple de hacer.

Sin mencionar que si quieres hacer animación, está integrada en la clase UIView.


Como MuscleRumble mencionó, la frontera de la respuesta aceptada está un poco fuera de MuscleRumble . Pude corregir esto al establecer el ancho de trazo en cero en lugar de cambiar el color a borrar.

es decir, reemplazando:

CGContextSetTextDrawingMode(c, kCGTextFill); self.textColor = textColor; self.shadowOffset = CGSizeMake(0, 0); [super drawTextInRect:rect];

con:

CGContextSetTextDrawingMode(c, kCGTextFillStroke); self.textColor = textColor; CGContextSetLineWidth(c, 0); // set stroke width to zero self.shadowOffset = CGSizeMake(0, 0); [super drawTextInRect:rect];

Acabo de comentar su respuesta, pero aparentemente no soy lo suficientemente "respetable".


Después de leer la respuesta aceptada y las dos correcciones y la respuesta de Axel Guilmin, decidí compilar una solución general en Swift, que me conviene:

import UIKit class UIOutlinedLabel: UILabel { var outlineWidth: CGFloat = 1 var outlineColor: UIColor = UIColor.whiteColor() override func drawTextInRect(rect: CGRect) { let strokeTextAttributes = [ NSStrokeColorAttributeName : outlineColor, NSStrokeWidthAttributeName : -1 * outlineWidth, ] self.attributedText = NSAttributedString(string: self.text ?? "", attributes: strokeTextAttributes) super.drawTextInRect(rect) } }

Puede agregar esta clase UILabel personalizada a una etiqueta existente en Interface Builder y cambiar el grosor del borde y su color agregando atributos de tiempo de ejecución definidos por el usuario como este:

Resultado:


Encontré un problema con la respuesta principal. La posición del texto no está necesariamente centrada correctamente en la ubicación de subpíxeles, por lo que el contorno puede no coincidir con el texto. Lo arreglé usando el siguiente código, que usa CGContextSetShouldSubpixelQuantizeFonts(ctx, false) :

- (void)drawTextInRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); [self.textOutlineColor setStroke]; [self.textColor setFill]; CGContextSetShouldSubpixelQuantizeFonts(ctx, false); CGContextSetLineWidth(ctx, self.textOutlineWidth); CGContextSetLineJoin(ctx, kCGLineJoinRound); CGContextSetTextDrawingMode(ctx, kCGTextStroke); [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment]; CGContextSetTextDrawingMode(ctx, kCGTextFill); [self.text drawInRect:rect withFont:self.font lineBreakMode:NSLineBreakByWordWrapping alignment:self.textAlignment]; }

Esto supone que ha definido textOutlineColor y textOutlineWidth como propiedades.


Esto no creará un esquema per se, pero pondrá una sombra alrededor del texto, y si hace que el radio de sombra sea lo suficientemente pequeño, podría parecerse a un contorno.

label.layer.shadowColor = [[UIColor blackColor] CGColor]; label.layer.shadowOffset = CGSizeMake(0.0f, 0.0f); label.layer.shadowOpacity = 1.0f; label.layer.shadowRadius = 1.0f;

No sé si es compatible con versiones anteriores de iOS ...

De todos modos, espero que ayude ...


Hay un problema con la implementación de la respuesta. Dibujar un texto con trazo tiene un ancho de glifo de carácter ligeramente diferente que dibujar un texto sin trazo, que puede producir resultados "no centrados". Se puede arreglar agregando un trazo invisible alrededor del texto de relleno.

Reemplazar:

CGContextSetTextDrawingMode(c, kCGTextFill); self.textColor = textColor; self.shadowOffset = CGSizeMake(0, 0); [super drawTextInRect:rect];

con:

CGContextSetTextDrawingMode(context, kCGTextFillStroke); self.textColor = textColor; [[UIColor clearColor] setStroke]; // invisible stroke self.shadowOffset = CGSizeMake(0, 0); [super drawTextInRect:rect];

No estoy 100% seguro, si ese es el verdadero negocio, porque no sé si self.textColor = textColor; tiene el mismo efecto que [textColor setFill] , pero debería funcionar.

Divulgación: soy el desarrollador de THLabel.

Hace un tiempo publiqué una subclase UILabel, que permite un esquema en texto y otros efectos. Puede encontrarlo aquí: https://github.com/tobihagemann/THLabel


Para poner un borde con bordes redondeados alrededor de un UILabel, hago lo siguiente:

labelName.layer.borderWidth = 1; labelName.layer.borderColor = [[UIColor grayColor] CGColor]; labelName.layer.cornerRadius = 10;

(no olvides incluir QuartzCore / QuartzCore.h)


Pude hacerlo anulando drawTextInRect:

- (void)drawTextInRect:(CGRect)rect { CGSize shadowOffset = self.shadowOffset; UIColor *textColor = self.textColor; CGContextRef c = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(c, 1); CGContextSetLineJoin(c, kCGLineJoinRound); CGContextSetTextDrawingMode(c, kCGTextStroke); self.textColor = [UIColor whiteColor]; [super drawTextInRect:rect]; CGContextSetTextDrawingMode(c, kCGTextFill); self.textColor = textColor; self.shadowOffset = CGSizeMake(0, 0); [super drawTextInRect:rect]; self.shadowOffset = shadowOffset; }


Si quieres animar algo complicado, la mejor manera es tomar una captura de pantalla de manera programática.

Para tomar una captura de pantalla de una vista, necesitará un código como este:

UIGraphicsBeginImageContext(mainContentView.bounds.size); [mainContentView.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();

Donde mainContentView es la vista de la que desea tomar una captura de pantalla. Añada viewImage a UIImageView y anímela.

Espero que acelere tu animación !!

norte


Una solución más simple es usar una Cadena Atribuida así:

let strokeTextAttributes = [ NSStrokeColorAttributeName : UIColor.blackColor(), NSForegroundColorAttributeName : UIColor.whiteColor(), NSStrokeWidthAttributeName : -1.0, ] myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

Sintaxis de Swift 4:

let strokeTextAttributes: [NSAttributedStringKey : Any] = [ NSAttributedStringKey.strokeColor : UIColor.black, NSAttributedStringKey.foregroundColor : UIColor.white, NSAttributedStringKey.strokeWidth : -2.0, ] myLabel.attributedText = NSAttributedString(string: "Foo", attributes: strokeTextAttributes)

En un UITextField puede establecer los defaultTextAttributes y el attributedPlaceholder también.

Tenga en cuenta que NSStrokeWidthAttributeName tiene que ser negativo en este caso, es decir, que solo funcionan los contornos internos.


si TODO lo que desea es un borde negro de un píxel alrededor de mi texto UILabel blanco,

entonces sí creo que estás haciendo que el problema sea más difícil de lo que es ... No sé por memoria qué función ''draw rect / frameRect'' debes usar, pero te será fácil encontrarla. este método simplemente demuestra la estrategia (¡deje que la superclase haga el trabajo!):

- (void)drawRect:(CGRect)rect { [super drawRect:rect]; [context frameRect:rect]; // research which rect drawing function to use... }