Podkreśl tekst w UIlabel

89

Jak mogę podkreślić tekst, który może składać się z wielu wierszy ciągu? Uważam, że niektórzy sugerują UIWebView, ale jest to oczywiście zbyt ciężka klasa do renderowania tylko tekstu.

Myślałem, że chciałem ustalić punkt początkowy i długość każdej struny w każdej linii. I odpowiednio narysuj pod nim linię.

Spotykam się z problemami, jak obliczyć długość i punkt początkowy struny.

Próbowałem użyć -[UILabel textRectForBounds:limitedToNumberOfLines:], to powinien być prostokąt ograniczający rysunek dla tekstu, prawda? Więc muszę popracować nad wyrównaniem? Jak mogę uzyskać punkt początkowy każdej linii, gdy jest wyjustowany do środka i do prawej?

semix
źródło
1
Spójrz na ten
post

Odpowiedzi:

137

Możesz podklasować z UILabel i nadpisać metodę 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:
Od iOS 6 Apple dodał obsługę NSAttributedString dla UILabel, więc teraz jest to znacznie łatwiejsze i działa dla wielu linii:

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

Jeśli nadal chcesz obsługiwać iOS 4 i iOS 5, polecam użycie TTTAttributedLabel zamiast ręcznego podkreślania etykiety. Jeśli jednak chcesz podkreślić jednowierszowy UILabel i nie chcesz używać komponentów innych firm, powyższy kod nadal by załatwił sprawę.

kovpas
źródło
3
Myślę, że spowoduje to tylko podkreślenie ostatniej linii ciągu, prawda? A co z podkreśleniem ciągu w innych wierszach?
semix
2
nie robi wielu linii, ale to najlepsze, co mogę znaleźć, więc myślę, że wiele linii nie wchodzi w rachubę. Myślę, że kolejnym najlepszym rozwiązaniem, jakie przychodzi mi do głowy, jest zaimportowanie czcionki, która ma podkreślenie wbudowane w czcionkę. Działa to tylko z iOS 4.0+, gdzie można importować czcionki.
DonnaLea
Cześć, chcę wiedzieć, czy to narusza którykolwiek ze standardów interfejsu użytkownika iOS.
thndrkiss
Implementacja Apple (druga sugestia) nie obsługuje znaków, które znajdują się poniżej linii? screencast.com/t/NGvQJqoWAD3J
pfrank
Jeśli używamy obsługi NSAttributedString dla UILabel, dla alfabetów takich jak g, p & q podkreślenie jest obcięte. Czy ktoś ma problem? Przykład: Login
dev4u
46

W Swift:

let underlineAttriString = NSAttributedString(string: "attriString",
                                          attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString
ytll21
źródło
Jedyne, co musisz zrobić w Swift 3, to zmienić .StyleSingle na .styleSingle, jest to camelCased w Swift3, ale świetna odpowiedź!
Josh O'Connor
Bez .rawValue powodowało to awarię.
jackofallcode
potrzebujesz tylko .rawValue dla swift 4.0
carrotzoe
Zbyt szczegółowe, by po prostu narysować podkreślenie.
khcpietro
38

To właśnie zrobiłem. Działa jak masło.

1) Dodaj CoreText.framework do swoich frameworków.

2) zaimportuj <CoreText / CoreText.h> do klasy, w której potrzebujesz podkreślonej etykiety.

3) Napisz następujący kod.

    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
źródło
+1 ode mnie za tę odpowiedź, ponieważ to rzeczywiście działa genialnie i pokazuje również łatwy sposób na ustawienie określonego zakresu znaków (czego sam potrzebowałem). Dzięki! - Erik
Erik van der Neut
19

Użyj ciągu atrybutu:

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

A następnie nadpisz etykietę - (void) drawTextInRect: (CGRect) aRect i wyrenderuj tekst na coś takiego:

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

Albo jeszcze lepiej, zamiast nadpisywać, po prostu użyj OHAttributedLabel stworzonej przez Oliviera Halligona

Paulo Ferreira
źródło
1
Górna linia powinna byćNSMutableAttributedString
borrrden
Powodem, dla którego zrezygnowałem z OHAttributedLabel, było to, że - przynajmniej dla mnie, nie było możliwe obliczenie dokładnej wysokości tekstu. w 10% było to nieprawidłowe. (może dlatego, że
używałem
15

Połączyłem niektóre z udzielonych odpowiedzi, aby stworzyć lepszą (przynajmniej jak na moje wymagania) podklasę UILabel, która obsługuje:

  • tekst wielowierszowy z różnymi granicami etykiety (tekst może znajdować się na środku ramki etykiety lub mieć dokładny rozmiar)
  • podkreślać
  • Skreślony
  • przesunięcie linii podkreślenia / przekreślenia
  • wyrównanie tekstu
  • różne rozmiary czcionek

https://github.com/GuntisTreulands/UnderLineLabel

Guntis Treulands
źródło
11

Osoby, które nie chcą tworzyć podklasy widoku (UILabel / UIButton) itp. ... „ForgetButton” mogą również zostać zastąpione przez dowolną etykietę.

-(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
źródło
2
-1 do wywołania metody „draw…”, która przydziela UILabel i dodaje ją do widoku.
jcayzac
1
Dostosowałem to, aby było nieco bardziej ogólne: oryginał pastebin.com/QkF9ifpb nie uwzględnia, czy etykieta jest w podglądzie.
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
źródło
7

Innym rozwiązaniem może być (od iOS 7) nadanie wartości ujemnej NSBaselineOffsetAttributeName, na przykład NSAttributedString:

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

Mam nadzieję, że to pomoże ;-)

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

Możesz stworzyć własną etykietę o nazwie UnderlinedLabel i edytować funkcję 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
źródło
3

Oto najłatwiejsze rozwiązanie, które działa u mnie bez pisania dodatkowych kodów.

// 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
źródło
3

Czasami deweloperzy utknęli w małej części projektowej dowolnego ekranu interfejsu użytkownika. Jednym z najbardziej irytujących wymagań jest tekst pod linią. Nie martw się, tutaj jest rozwiązanie.

wprowadź opis obrazu tutaj

Podkreślenie tekstu w UILabel przy użyciu celu 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];

Podkreślanie tekstu w UILabel za pomocą 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
Panie Javed Multani
źródło
1

Ulepszona wersja kodu Kovpas (kolor i rozmiar linii)

@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
źródło
1

Stworzyłem dla wielowierszowego uilabel z podkreśleniem:

Dla rozmiaru czcionki od 8 do 13 ustaw int lineHeight = self.font.pointSize + 3;

Dla rozmiaru czcionki od 14 do 20 ustaw 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
źródło
0

Jak pokazał kovpas, w większości przypadków możesz użyć obwiedni, chociaż nie zawsze jest gwarantowane, że obwiednia będzie dokładnie dopasowana do tekstu. Ramka o wysokości 50 i rozmiarze czcionki 12 może nie dawać oczekiwanych wyników w zależności od konfiguracji UILabel.

Zapytaj o UIString w UILabel, aby określić jego dokładne metryki i użyj ich do lepszego umieszczenia podkreślenia niezależnie od otaczającej ramki ograniczającej lub ramki, używając kodu rysunkowego już dostarczonego przez kovpas.

Powinieneś także przyjrzeć się właściwości „wiodącej” UIFont, która podaje odległość między liniami bazowymi w oparciu o określoną czcionkę. Linia bazowa to miejsce, w którym chcesz narysować podkreślenie.

Wyszukaj dodatki UIKit do 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.
zgrzytacz
źródło
Wygląda na to, że Kenny mogę użyć trzech metod, aby łatwo uzyskać szerokość pierwszego wiersza tekstu, ale co z drugim trzecim i innymi wierszami? Czy możesz podać przykład?
semix
Muszę przyznać. Jest teraz sposób na wykorzystanie NSString do osiągnięcia tego, co chcesz, chyba że ktoś inny ma więcej do zaoferowania. Będę musiał zasugerować, podobnie jak inni przede mną, użycie UIWebView i umieszczenie tekstu w widoku: [webView loadHTMLString: @ "<html> <u> Podkreślony tekst. </u> </html>" baseURL: nil ]; Niech zajmie się układem i określeniem, gdzie powinny iść linie. Jeśli chodzi o Ciebie, chcesz podkreślić n-ty wiersz i nie możesz wiedzieć, która jest n-tą linią, to już inna sprawa.
gnasher
0

Używam widoku liniowego typu open source i właśnie dodałem go do podglądów przycisków:

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

To było zainspirowane przez Karima powyżej.

David H.
źródło
Możesz po prostu użyć UIVIew. UIView * line = [[UIView assign] initWithFrame: frame]; line.backgroundColor = [UIColor lightGrayColor];
dzeikei
0

W oparciu o odpowiedzi Kovpasa i Damiena Praca, oto implementacja UILabelUnderligned, która obsługuje również textAlignemnt .

#import <UIKit/UIKit.h>

@interface UILabelUnderlined : UILabel

@end

i realizacja:

#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
źródło
Aktualizacja dla iOS 6 dla switch: switch (self.textAlignment) {case NSTextAlignmentLeft: case NSTextAlignmentJustified: case NSTextAlignmentNatural: break; case NSTextAlignmentCenter: alignementXOffset = (self.titleLabel.frame.size.width - textSize.width) / 2; złamać; case NSTextAlignmentRight: alignementXOffset = self.titleLabel.frame.size.width - textSize.width; złamać; }
pfrank
0

Oto inne, prostsze rozwiązanie (szerokość podkreślenia nie jest najdokładniejsza, ale dla mnie wystarczyła)

Mam UIView, (_view_underline)który ma białe tło, wysokość 1 piksela i aktualizuję jego szerokość za każdym razem, gdy aktualizuję tekst

// 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
źródło
0

NSUnderlineStyleAttributeName, który przyjmuje numer NSNumber (gdzie 0 nie jest podkreśleniem), można dodać do słownika atrybutów. Nie wiem, czy to jest łatwiejsze. Ale dla moich celów było to łatwiejsze.

    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
źródło
0

Wersja Swift 4.1:

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

label.attributedText = underlineAttriString
Abdoelrhman
źródło
0

Możesz użyć tej mojej niestandardowej etykiety! Do ustawienia możesz również użyć konstruktora interfejsu

import UIKit


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

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

}
Ucdemir
źródło