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?


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ę.


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

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

+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


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


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

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

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


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

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

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;

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

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

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

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.

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.


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

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

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

0

Wersja Swift 4.1:

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

label.attributedText = underlineAttriString

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])
        }
        
        
    }

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.