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?

semix
fuente
1
Mire esta entrada de
blog

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.

kovpas
fuente
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
ytll21
fuente
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];
Sana
fuente
+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

Paulo Ferreira
fuente
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

Guntis Treulands
fuente
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];
}
karim
fuente
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;
}
Jill Wong
fuente
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 ;-)

youssman
fuente
7
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;
Roman Solodyashkin
fuente
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];
}
nfinfu
fuente
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;
Dhaval Dobariya
fuente
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
Señor Javed Multani
fuente
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
Damien Praca
fuente
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]; 
}
Piyush
fuente
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.
gnasher
fuente
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.

David H
fuente
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
Pascal
fuente
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];
}
Ege Akpinar
fuente
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];
epaus
fuente
0

Swift 4.1 versión:

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

label.attributedText = underlineAttriString
Abdoelrhman
fuente
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])
        }
        
        
    }

}
Ucdemir
fuente