Sublinhar o texto no UIlabel

89

Como posso sublinhar um texto que pode ter várias linhas de string? Acho que algumas pessoas sugerem UIWebView, mas obviamente é uma classe muito pesada para apenas renderização de texto.

Meu pensamento era descobrir o ponto inicial e o comprimento de cada corda em cada linha. E desenhe uma linha em conformidade

Encontro problemas em como descobrir o comprimento e o ponto inicial da corda.

Tentei usar -[UILabel textRectForBounds:limitedToNumberOfLines:], este deve ser o retângulo delimitador do desenho certo? Então eu tenho que trabalhar no alinhamento? Como posso obter o ponto inicial de cada linha quando ela está justificada ao centro e à direita?

semix
fonte
1
Olhe para esta postagem do blog
Casebash,

Respostas:

137

Você pode criar uma subclasse de UILabel e substituir o 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 do iOS 6, a Apple adicionou suporte a NSAttributedString para UILabel, então agora é muito mais fácil e funciona para várias linhas:

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

Se você ainda deseja oferecer suporte ao iOS 4 e iOS 5, recomendo usar TTTAttributedLabel em vez de sublinhar o rótulo manualmente. No entanto, se você precisar sublinhar UILabel de uma linha e não quiser usar componentes de terceiros, o código acima ainda resolverá o problema.

kovpas
fonte
3
Eu acho que isso só vai desenhar um sublinhado para a última linha da corda, certo? E o sublinhado para a string nas outras linhas?
semix
2
ele não faz várias linhas, mas isso é o melhor que posso encontrar, então acho que várias linhas estão fora de questão. Acho que a próxima melhor solução que posso pensar é importar uma fonte com um sublinhado embutido na fonte. Isso só funcionaria a partir do ios 4.0+, onde você pode importar fontes.
DonnaLea,
oi, gostaria de saber se isso viola algum dos padrões da interface do usuário do iOS.
thndrkiss,
A implementação da Apple (a segunda sugestão) não oferece suporte a caracteres que vão abaixo da linha? screencast.com/t/NGvQJqoWAD3J
pfrank
Se usarmos o suporte NSAttributedString para UILabel, para alfabetos como g, p & q sublinhado é truncado. Alguém está enfrentando o problema? Exemplo: Login
dev4u
46

Em Swift:

let underlineAttriString = NSAttributedString(string: "attriString",
                                          attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString
ytll21
fonte
A única coisa que você precisa fazer no Swift 3 é mudar .StyleSingle para .styleSingle, é camelCased em Swift3, mas ótima resposta!
Josh O'Connor
Sem .rawValue, isso estava causando uma falha para mim.
jackofallcode
você só precisaria de .rawValue para swift 4.0
carrotzoe
Muito prolixo para apenas desenhar sublinhado.
khcpietro
38

Isso é o que eu fiz. Funciona como manteiga.

1) Adicione CoreText.framework aos seus Frameworks.

2) importe <CoreText / CoreText.h> na classe onde você precisa do rótulo sublinhado.

3) Escreva o seguinte 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
fonte
+1 de mim para esta resposta, porque isso realmente funciona brilhantemente e demonstra uma maneira fácil de definir um intervalo específico de caracteres também (que é o que eu mesmo precisava). Obrigado! - Erik
Erik van der Neut
19

Use uma string de atributo:

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

Em seguida, substitua o rótulo - (void) drawTextInRect: (CGRect) aRect e renderize o texto em 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);

Ou melhor ainda, em vez de substituir, basta usar o OHAttributedLabel criado por Olivier Halligon

Paulo ferreira
fonte
1
A linha superior deve serNSMutableAttributedString
borrrden
A razão pela qual abandonei o uso do OHAttributedLabel foi que - pelo menos para mim, não era possível calcular a altura exata do texto. em 10% dos casos estava incorreto. (talvez porque eu estava usando uma fonte diferente ..)
Guntis Treulands
15

Combinei algumas das respostas fornecidas para criar uma subclasse UILabel melhor (pelo menos para os meus requisitos), que suporta:

  • texto multilinha com vários limites de rótulo (o texto pode estar no meio do quadro do rótulo ou de tamanho preciso)
  • sublinhado
  • riscado
  • deslocamento da linha sublinhada / riscada
  • alinhamento do texto
  • tamanhos de fonte diferentes

https://github.com/GuntisTreulands/UnderLineLabel

Guntis Treulands
fonte
11

Pessoas que não querem uma subclasse da visão (UILabel / UIButton) etc ... 'forgetButton' pode ser substituído por qualquer lable também.

-(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
fonte
2
-1 para chamar "draw ..." um método que aloca um UILabel e o adiciona à visão.
jcayzac
1
Eu adaptei isso para ser um pouco mais genérico: pastebin.com/QkF9ifpb original não leva em conta se o rótulo está em uma subvisualização.
Fonix de
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
fonte
7

Outra solução poderia ser (desde iOS 7) dado um valor negativo para NSBaselineOffsetAttributeName, por exemplo, seu NSAttributedStringpoderia 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 isso ajude ;-)

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

Você pode criar um rótulo personalizado com o nome UnderlinedLabel e editar a função 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
fonte
3

Aqui está a solução mais fácil que funciona para mim sem escrever códigos adicionais.

// 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
fonte
3

Às vezes, nós desenvolvedores ficamos presos em uma pequena parte de design de qualquer tela da IU. Um dos requisitos mais irritantes é o texto de linha. Não se preocupe, aqui está a solução.

insira a descrição da imagem aqui

Sublinhando um texto em um 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];

Sublinhando um texto em 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
Sr.Javed Multani
fonte
1

Uma versão aprimorada do código de Kovpas (cor e tamanho da linha)

@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
fonte
1

Eu criei para uilabel multilinha com sublinhado:

Para tamanho de fonte 8 a 13, defina int lineHeight = self.font.pointSize + 3;

Para o tamanho da fonte de 14 a 20, defina 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
fonte
0

Como kovpas mostrou, você pode usar a caixa delimitadora na maioria dos casos, embora nem sempre seja garantido que a caixa delimitadora se encaixará perfeitamente ao redor do texto. Uma caixa com uma altura de 50 e tamanho de fonte de 12 pode não dar os resultados desejados dependendo da configuração do UILabel.

Consulte o UIString dentro do UILabel para determinar suas métricas exatas e use-as para posicionar melhor o seu sublinhado, independentemente da caixa delimitadora ou do quadro envolvente, usando o código de desenho já fornecido por kovpas.

Você também deve observar a propriedade "principal" do UIFont, que fornece a distância entre as linhas de base com base em uma fonte específica. A linha de base é onde você deseja que seu sublinhado seja desenhado.

Procure as adições 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
fonte
Kenny, parece que posso usar os 3 métodos para obter a largura da 1ª linha de texto facilmente, mas e a 2ª 3ª e outras linhas? Você pode dar um exemplo?
semix
Eu tenho que admitir. Agora existe uma maneira de usar NSString para alcançar o que você deseja, a menos que alguém tenha mais a oferecer. Vou ter que sugerir, como os outros antes de mim, usar UIWebView e colocar seu texto na visualização: [webView loadHTMLString: @ "<html> <u> Texto sublinhado. </u> </html>" baseURL: nil ]; Deixe-o fazer o layout e determinar onde as linhas devem ir. Se é uma questão de você querer a enésima linha sublinhada e não pode saber qual é a enésima linha, isso é outro assunto.
gnasher
0

Eu uso uma visualização de linha de código aberto e acabei de adicioná-la às subvisualizações do botão:

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

Isso foi inspirado por Karim acima.

David H
fonte
Você pode apenas usar UIVIew. UIView * line = [[UIView alloc] initWithFrame: frame]; line.backgroundColor = [UIColor lightGrayColor];
dzeikei
0

Com base nas respostas de Kovpas e Damien Praca, aqui está uma implementação de UILabelUnderligned que também oferece suporte a textAlignemnt .

#import <UIKit/UIKit.h>

@interface UILabelUnderlined : UILabel

@end

e a implementação:

#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
fonte
Atualização 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; pausa; case NSTextAlignmentRight: alignementXOffset = self.titleLabel.frame.size.width - textSize.width; pausa; }
pfrank
0

Aqui está outra solução mais simples (a largura do sublinhado não é a mais precisa, mas foi boa o suficiente para mim)

Eu tenho um UIView (_view_underline)que tem fundo branco, altura de 1 pixel e eu atualizo sua largura toda vez que atualizo o 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
fonte
0

NSUnderlineStyleAttributeName que leva um NSNumber (onde 0 é nenhum sublinhado) pode ser adicionado a um dicionário de atributos. Não sei se isso é mais fácil. Mas, era mais fácil para meus 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
fonte
0

Swift 4.1 versão:

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

label.attributedText = underlineAttriString
Abdoelrhman
fonte
0

Você pode usar este meu rótulo personalizado! Você também pode usar o construtor de interface para definir

import UIKit


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

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

}
Ucdemir
fonte