Subrayar texto en UIlabel


89

¿Cómo puedo subrayar un texto que podría tener varias líneas de cadena? Encuentro que algunas personas sugieren UIWebView, pero obviamente es una clase demasiado pesada para solo representar texto.

Mi pensamiento era averiguar el punto de inicio y la longitud de cada cuerda en cada línea. Y dibuja una línea debajo de ella en consecuencia.

Encuentro problemas sobre cómo calcular la longitud y el punto de inicio de la cuerda.

Intenté usar -[UILabel textRectForBounds:limitedToNumberOfLines:], este debería ser el rectángulo delimitador del dibujo para el texto, ¿verdad? ¿Entonces tengo que trabajar en la alineación? ¿Cómo puedo obtener el punto de inicio de cada línea cuando está justificado al centro y a la derecha?


Respuestas:


137

Puede crear una subclase de UILabel y anular el método drawRect:

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetRGBStrokeColor(ctx, 207.0f/255.0f, 91.0f/255.0f, 44.0f/255.0f, 1.0f); // RGBA
    CGContextSetLineWidth(ctx, 1.0f);

    CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, self.bounds.size.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}

UPD:
A partir de iOS 6, Apple agregó soporte NSAttributedString para UILabel, por lo que ahora es mucho más fácil y funciona para múltiples líneas:

NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
myLabel.attributedText = [[NSAttributedString alloc] initWithString:@"Test string" 
                                                         attributes:underlineAttribute];

Si aún desea admitir iOS 4 y iOS 5, le recomiendo usar TTTAttributedLabel en lugar de subrayar la etiqueta manualmente. Sin embargo, si necesita subrayar UILabel de una línea y no desea utilizar componentes de terceros, el código anterior aún funcionaría.


3
Supongo que esto solo dibujará un subrayado para la última línea de cadena, ¿verdad? ¿Qué pasa con el subrayado de la cadena en otras líneas?
semix

2
no hace varias líneas, pero esta es la mejor que puedo encontrar, así que supongo que varias líneas están fuera de cuestión. Supongo que la siguiente mejor solución en la que puedo pensar es importar una fuente que tenga un subrayado integrado en la fuente. Esto solo funcionaría desde ios 4.0+, donde puede importar fuentes.
DonnaLea

hola, quiero saber si esto viola alguno de los estándares de ios ui.
thndrkiss

¿La implementación de Apple (la segunda sugerencia) no admite caracteres que van por debajo de la línea? screencast.com/t/NGvQJqoWAD3J
pfrank

Si usamos el soporte NSAttributedString para UILabel, para alfabetos como g, p & q el subrayado se trunca. ¿Alguien enfrenta el problema? Ejemplo: Iniciar sesión
dev4u

46

En Swift:

let underlineAttriString = NSAttributedString(string: "attriString",
                                          attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString

Lo único que tienes que hacer en Swift 3 es cambiar .StyleSingle a .styleSingle, es camelCased en Swift3, ¡pero excelente respuesta!
Josh O'Connor

Sin .rawValue, esto me estaba causando un bloqueo.
jackofallcode

solo necesitaría .rawValue para swift 4.0
carrotzoe

Demasiado detallado para simplemente dibujar un subrayado.
khcpietro

38

Esto es lo que hice. Funciona como mantequilla.

1) Agregue CoreText.framework a sus Frameworks.

2) importe <CoreText / CoreText.h> en la clase donde necesita la etiqueta subrayada.

3) Escribe el siguiente código.

    NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"My Messages"];
    [attString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
              value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
              range:(NSRange){0,[attString length]}];
    self.myMsgLBL.attributedText = attString;
    self.myMsgLBL.textColor = [UIColor whiteColor];

+1 de mí para esta respuesta, porque esto de hecho funciona de manera brillante, y demuestra una manera fácil de establecer un rango de caracteres específico también (que es lo que yo necesitaba). ¡Gracias! - Erik
Erik van der Neut

19

Utilice una cadena de atributos:

NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:@"Your String"]
[attrString addAttribute:(NSString*)kCTUnderlineStyleAttributeName 
                   value:[NSNumber numberWithInt:kCTUnderlineStyleSingle] 
                   range:(NSRange){0,[attrString length]}];

Y luego anule la etiqueta - (void) drawTextInRect: (CGRect) aRect y renderice el texto en algo como:

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
drawingRect = self.bounds;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, drawingRect);
textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
CTFrameDraw(textFrame, ctx);
CGContextRestoreGState(ctx);

O mejor aún, en lugar de anular, simplemente use OHAttributedLabel creado por Olivier Halligon


1
La línea superior debería serNSMutableAttributedString
borrrden

La razón por la que dejé de usar OHAttributedLabel fue que, al menos para mí, no era posible calcular la altura del texto con precisión. en el 10% de los casos fue incorrecto. (tal vez porque estaba usando una fuente diferente ..)
Guntis Treulands

15

He combinado algunas de las respuestas proporcionadas para crear una mejor subclase de UILabel (al menos para mis requisitos), que admite:

  • texto de varias líneas con varios límites de etiqueta (el texto puede estar en el medio del marco de la etiqueta o en un tamaño exacto)
  • subrayar
  • tachar
  • Desplazamiento de línea de subrayado / tachado
  • alineación del texto
  • diferentes tamaños de fuente

https://github.com/GuntisTreulands/UnderLineLabel


11

Las personas que no quieran subclasificar la vista (UILabel / UIButton), etc ... 'ForgetButton' también puede ser reemplazado por cualquier etiqueta.

-(void) drawUnderlinedLabel {
    NSString *string = [forgetButton titleForState:UIControlStateNormal];
    CGSize stringSize = [string sizeWithFont:forgetButton.titleLabel.font];
    CGRect buttonFrame = forgetButton.frame;
    CGRect labelFrame = CGRectMake(buttonFrame.origin.x + buttonFrame.size.width - stringSize.width, 
            buttonFrame.origin.y + stringSize.height + 1 , 
            stringSize.width, 2);
    UILabel *lineLabel = [[UILabel alloc] initWithFrame:labelFrame];
    lineLabel.backgroundColor = [UIColor blackColor];
    //[forgetButton addSubview:lineLabel];
    [self.view addSubview:lineLabel];
}

2
-1 para llamar a "dibujar ..." un método que asigna un UILabel y lo agrega a la vista.
jcayzac

1
He adaptado esto para que sea un poco más genérico: pastebin.com/QkF9ifpb original no tiene en cuenta si la etiqueta está en una subvista.
Fonix

8
NSString *tem =self.detailCustomerCRMCaseLabel.text;
if (tem != nil && ![tem isEqualToString:@""]) {
    NSMutableAttributedString *temString=[[NSMutableAttributedString alloc]initWithString:tem];
    [temString addAttribute:NSUnderlineStyleAttributeName
                      value:[NSNumber numberWithInt:1]
                      range:(NSRange){0,[temString length]}];
    self.detailCustomerCRMCaseLabel.attributedText = temString;
}

7

Otra solución podría ser (desde iOS 7) dar un valor negativo a NSBaselineOffsetAttributeName, por ejemplo, NSAttributedStringpodría ser:

NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:@"my text goes here'
                                                            attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Helvetica-Regular" size:12],
                                                                         NSForegroundColorAttributeName: [UIColor blackColor],
                                                                         NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSBaselineOffsetAttributeName: @(-3)}];

Espero que esto ayude ;-)


7
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;

3

Puede crear una etiqueta personalizada con el nombre UnderlinedLabel y editar la función drawRect.

#import "UnderlinedLabel.h"

@implementation UnderlinedLabel

- (void)drawRect:(CGRect)rect
{
   NSString *normalTex = self.text;
   NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
   self.attributedText = [[NSAttributedString alloc] initWithString:normalTex
                                                      attributes:underlineAttribute];

   [super drawRect:rect];
}

3

Esta es la solución más sencilla que me funciona sin escribir códigos adicionales.

// To underline text in UILable
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Type your text here"];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
lblText.attributedText = text;

3

A veces, el desarrollador se atasca en una pequeña parte de diseño de cualquier pantalla de interfaz de usuario. Uno de los requisitos más irritantes es el texto debajo de la línea. No se preocupe, aquí está la solución.

ingrese la descripción de la imagen aquí

Subrayando un texto en un UILabel usando Objective C

UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
label.backgroundColor=[UIColor lightGrayColor];
NSMutableAttributedString *attributedString;
attributedString = [[NSMutableAttributedString alloc] initWithString:@"Apply Underlining"];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:@1 range:NSMakeRange(0,
[attributedString length])];
[label setAttributedText:attributedString];

Subrayar un texto en UILabel usando Swift

 label.backgroundColor = .lightGray
 let attributedString = NSMutableAttributedString.init(string: "Apply UnderLining")
 attributedString.addAttribute(NSUnderlineStyleAttributeName, value: 1, range:
NSRange.init(location: 0, length: attributedString.length))
 label.attributedText = attributedString

1

Una versión mejorada del código de Kovpas (color y tamaño de línea)

@implementation UILabelUnderlined

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

    CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

    CGContextSetLineWidth(ctx, 1.0f);

    CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];

    CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, tmpSize.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}

@end

1

He creado para uilabel multilínea con subrayado:

Para el tamaño de fuente de 8 a 13, establezca int lineHeight = self.font.pointSize + 3;

Para tamaños de fuente de 14 a 20, establezca int lineHeight = self.font.pointSize + 4;

- (void)drawRect:(CGRect)rect 

{

CGContextRef ctx = UIGraphicsGetCurrentContext();

const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999)];

int height = tmpSize.height;

int lineHeight = self.font.pointSize+4;    

int maxCount = height/lineHeight;

float totalWidth = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(1000, 9999)].width;

for(int i=1;i<=maxCount;i++)

{

    float width=0.0;
    if((i*self.frame.size.width-totalWidth)<=0)
        width = self.frame.size.width;
    else
        width = self.frame.size.width - (i* self.frame.size.width - totalWidth);
    CGContextMoveToPoint(ctx, 0, lineHeight*i-1);
    CGContextAddLineToPoint(ctx, width, lineHeight*i-1);
}

CGContextStrokePath(ctx);

[super drawRect:rect]; 
}

0

Como ha demostrado kovpas, puede usar el cuadro delimitador en la mayoría de los casos, aunque no siempre se garantiza que el cuadro delimitador se ajuste perfectamente alrededor del texto. Es posible que un cuadro con una altura de 50 y un tamaño de fuente de 12 no proporcione los resultados que desea según la configuración de UILabel.

Consulte el UIString dentro de UILabel para determinar sus métricas exactas y utilícelas para colocar mejor su subrayado, independientemente del cuadro delimitador o marco adjunto, utilizando el código de dibujo ya proporcionado por kovpas.

También debe observar la propiedad "principal" de UIFont que proporciona la distancia entre líneas de base según una fuente en particular. La línea de base es donde le gustaría que se dibujara su subrayado.

Busque las adiciones de UIKit a NSString:

(CGSize)sizeWithFont:(UIFont *)font 
//Returns the size of the string if it were to be rendered with the specified font on a single line.

(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size 
// Returns the size of the string if it were rendered and constrained to the specified size.

(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
//Returns the size of the string if it were rendered with the specified constraints.

Kenny, parece que puedo usar los 3 métodos para obtener el ancho de la primera línea de texto fácilmente, pero ¿qué tal la segunda tercera y otras líneas? ¿Puede dar un ejemplo?
semix

Tengo que conceder. Ahora existe una forma de usar NSString para lograr lo que desea, a menos que alguien más tenga más que ofrecer. Voy a tener que sugerir, como los demás antes que yo, usar UIWebView y colocar su texto en la vista: [webView loadHTMLString: @ "<html> <u> Texto subrayado. </u> </html>" baseURL: nil ]; Deje que haga el diseño y la determinación de dónde deben ir las líneas. Si se trata de que desea subrayar la enésima línea y no puede saber cuál es la enésima línea, eso es otro asunto.
gnasher

0

Utilizo una vista de línea de código abierto y simplemente la agregué a las subvistas de botones:

 UILabel *label = termsButton.titleLabel;
 CGRect frame = label.frame;
 frame.origin.y += frame.size.height - 1;
 frame.size.height = 1;
 SSLineView *line = [[SSLineView alloc] initWithFrame:frame];
 line.lineColor = [UIColor lightGrayColor];
 [termsButton addSubview:line];

Esto fue inspirado por Karim arriba.


Podrías usar UIVIew. UIView * línea = [[UIView alloc] initWithFrame: frame]; line.backgroundColor = [UIColor lightGrayColor];
dzeikei

0

Basado en las respuestas de Kovpas y Damien Praca, aquí hay una implementación de UILabelUnderligned que también es compatible con textAlignemnt .

#import <UIKit/UIKit.h>

@interface UILabelUnderlined : UILabel

@end

y la implementación:

#import "UILabelUnderlined.h"

@implementation DKUILabel

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

    CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

    CGContextSetLineWidth(ctx, 1.0f);

    CGSize textSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];

    // handle textAlignement

    int alignementXOffset = 0;

    switch (self.textAlignment) {
        case UITextAlignmentLeft:
            break;
        case UITextAlignmentCenter:
            alignementXOffset = (self.frame.size.width - textSize.width)/2;
            break;
        case UITextAlignmentRight:
            alignementXOffset = self.frame.size.width - textSize.width;
            break;
    }

    CGContextMoveToPoint(ctx, alignementXOffset, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, alignementXOffset+textSize.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}


@end

Actualización para iOS 6 para switch: switch (self.textAlignment) {case NSTextAlignmentLeft: case NSTextAlignmentJustified: case NSTextAlignmentNatural: break; case NSTextAlignmentCenter: alignementXOffset = (self.titleLabel.frame.size.width - textSize.width) / 2; romper; case NSTextAlignmentRight: alignementXOffset = self.titleLabel.frame.size.width - textSize.width; romper; }
pfrank

0

Aquí hay otra solución más simple (el ancho del subrayado no es el más preciso, pero fue lo suficientemente bueno para mí)

Tengo una UIView (_view_underline)que tiene un fondo blanco, una altura de 1 píxel y actualizo su ancho cada vez que actualizo el texto

// It's a shame you have to do custom stuff to underline text
- (void) underline  {
    float width = [[_txt_title text] length] * 10.0f;
    CGRect prev_frame = [_view_underline frame];
    prev_frame.size.width = width;
    [_view_underline setFrame:prev_frame];
}

0

NSUnderlineStyleAttributeName que toma un NSNumber (donde 0 no es subrayado) se puede agregar a un diccionario de atributos. No sé si esto es más fácil. Pero fue más fácil para mis propósitos.

    NSDictionary *attributes; 
    attributes = @{NSFontAttributeName:font,   NSParagraphStyleAttributeName: style, NSUnderlineStyleAttributeName:[NSNumber numberWithInteger:1]};

    [text drawInRect:CGRectMake(self.contentRect.origin.x, currentY, maximumSize.width, textRect.size.height) withAttributes:attributes];

0

Swift 4.1 versión:

 let underlineAttriString = NSAttributedString(string:"attriString", attributes:
    [NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])

label.attributedText = underlineAttriString

0

¡Puedes usar esta mi etiqueta personalizada! También puede utilizar el generador de interfaces para configurar

import UIKit


class  YHYAttributedLabel : UILabel{
    
    
    @IBInspectable
    var underlineText : String = ""{
        
        didSet{

            self.attributedText = NSAttributedString(string: underlineText,
            attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
        }
        
        
    }

}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.