By semix


2010-04-26 05:33:18 8 Comments

How can I underline a text that could be multiple lines of string? I find some people suggest UIWebView, but it is obviously too heavy a class for just text rendering.

My thoughts was to figure out the start point and length of each string in each line. And draw a line under it accordingly.

I meet problems at how to figure out the length and start point for the string. Can anybody help me on this?

I tried to use -[UILabel textRectForBounds:limitedToNumberOfLines:], this should be the drawing bounding rect for the text right? Then I have to work on the alignment? How can I get the start point of each line when it is center-justified and right justified?

I am new here, so thank you in advance.

19 comments

@ytll21 2014-11-22 08:51:33

In Swift:

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

@Josh O'Connor 2017-03-21 22:39:32

The only thing you have to do in Swift 3 is change .StyleSingle to .styleSingle, it is camelCased in Swift3, but great answer!

@jackofallcode 2017-10-31 15:11:35

Without .rawValue, this was causing a crash for me.

@carrotzoe 2018-08-08 18:12:38

you would only need .rawValue for swift 4.0

@Abdoelrhman 2018-09-12 10:45:19

Swift 4.1 ver:

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

label.attributedText = underlineAttriString

@epaus 2017-07-18 23:39:29

NSUnderlineStyleAttributeName which takes an NSNumber (where 0 is no underline) can be added to an attribute dictionary. I don't know if this is any easier. But, it was easier for my purposes.

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

@Dhaval Dobariya 2016-10-06 10:24:30

Here is the easiest solution which works for me without writing additional codes.

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

@Roman Solodyashkin 2016-04-11 13:38:59

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

@nfinfu 2016-01-11 09:48:08

You can create a custom label with name UnderlinedLabel and edit drawRect function.

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

@youssman 2015-04-21 17:14:44

Another solution could be (since iOS 7) given a negative value to NSBaselineOffsetAttributeName, for example your NSAttributedString could be:

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

Hope this will help ;-)

@kovpas 2010-04-26 06:59:05

You may subclass from UILabel and override drawRect method:

- (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:
As of iOS 6 Apple added NSAttributedString support for UILabel, so now it's much easier and works for multiple lines:

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

If you still wish to support iOS 4 and iOS 5, I'd recommend to use TTTAttributedLabel rather than underline label manually. However if you need to underline one-line UILabel and don't want to use third-party components, code above would still do the trick.

@semix 2010-04-26 07:29:16

I guess this will only draw one underline for the last line of string, right? What about the underline for the string in other lines?

@DonnaLea 2011-09-08 05:45:51

it doens't do multiple lines, but this is the best I can find, so I guess multiple lines is out of the question. I guess the next best solution I can think of is to import a font which has an underline built into the font. This would only work from ios 4.0+ where you can import fonts.

@thndrkiss 2011-10-02 07:25:33

hi, i want to know whether this violates any of the ios ui standards.

@kovpas 2011-10-02 22:18:40

@thndrkiss, don't think so.

@Ben Lachman 2012-08-25 03:23:02

Doesn't do multiple lines.

@pfrank 2013-10-13 16:58:56

Apple's implementation (the second suggestion) doesn't support characters that go below the line? screencast.com/t/NGvQJqoWAD3J

@dev4u 2014-03-10 09:34:04

If we use NSAttributedString support for UILabel, for alphabets like g,p & q underline is truncating. Any one facing the issue? Example: Login

@Sana 2013-10-28 08:47:46

This is what i did. It works like butter.

1) Add CoreText.framework to your Frameworks.

2) import <CoreText/CoreText.h> in the class where you need underlined label.

3) Write the following code.

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

@Erik van der Neut 2015-01-22 23:34:44

+1 from me for this answer, because this indeed works brilliantly, and it demonstrates an easy way to set a specific character range as well (which is what I needed myself). Thanks! -- Erik

@Jill Wong 2013-02-20 08:43:11

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

@odyth 2013-03-12 23:28:05

FYI, This only works on iOS 6.0+

@Paulo Ferreira 2012-04-11 12:08:00

Use an attribute string:

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

And then override the label - (void)drawTextInRect:(CGRect)aRect and render the text in something like:

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

Or better yet instead of overriding just use the OHAttributedLabel created by Olivier Halligon

@borrrden 2012-05-16 02:40:48

The top line should be NSMutableAttributedString

@Guntis Treulands 2012-09-28 13:11:42

The reason I dropped using OHAttributedLabel was, that - atleast for me, it wasn't possible to calculate accurate text height. in 10% cases it was incorrect. (maybe because I was using different font.. )

@Ege Akpinar 2012-11-14 09:29:27

Here's another, simpler solution (underline's width is not most accurate but it was good enough for me)

I have a UIView (_view_underline) that has White background, height of 1 pixel and I update its width everytime I update the text

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

@Pascal 2012-07-19 12:56:36

Based on Kovpas & Damien Praca's Answers, here is an implementation of UILabelUnderligned which also support textAlignemnt.

#import <UIKit/UIKit.h>

@interface UILabelUnderlined : UILabel

@end

and the implementation:

#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

@pfrank 2013-10-13 17:06:26

Update for iOS 6 for switch: switch (self.textAlignment) { case NSTextAlignmentLeft: case NSTextAlignmentJustified: case NSTextAlignmentNatural: break; case NSTextAlignmentCenter: alignementXOffset = (self.titleLabel.frame.size.width - textSize.width)/2; break; case NSTextAlignmentRight: alignementXOffset = self.titleLabel.frame.size.width - textSize.width; break; }

@Guntis Treulands 2012-08-06 10:56:11

I've combined some of provided answers, to create better (at least for my requirements) UILabel subclass, which supports:

  • multiline text with various label bounds (text can be in the middle of label frame, or accurate size)
  • underline
  • strikeout
  • underline/strikeout line offset
  • text alignment
  • different font sizes

https://github.com/GuntisTreulands/UnderLineLabel

@turingtested 2015-06-25 18:48:04

The code is outdated and doesn't work.

@Guntis Treulands 2015-06-25 20:06:54

Still works on my end.

@karim 2011-01-11 15:36:21

People, who do not want to subclass the view (UILabel/UIButton) etc... 'forgetButton' can be replace by any lable too.

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

@jcayzac 2012-11-14 05:45:43

-1 for calling "draw…" a method that allocates a UILabel and adds it to the view.

@Fonix 2013-02-08 12:58:32

I've adapted this to be a bit more generic: pastebin.com/QkF9ifpb original doesn't account for if the label is in a subview.

@David H 2011-10-07 18:40:32

I use an open source line view and just added it to the button subviews:

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

This was inspired by Karim above.

@dzeikei 2012-03-01 01:44:01

You could just use UIVIew. UIView *line = [[UIView alloc] initWithFrame:frame]; line.backgroundColor = [UIColor lightGrayColor];

@Piyush 2011-08-25 07:32:26

I have Created for multiline uilabel with underline :

For Font size 8 to 13 set int lineHeight = self.font.pointSize+3;

For font size 14 to 20 set 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]; 
}

@Damien Praca 2011-05-19 13:04:20

An enhanced version of the code of Kovpas (color and line size)

@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

@Ben Lachman 2012-08-25 03:22:36

Doesn't do multiple lines.

@gnasher 2010-04-26 07:34:23

As kovpas has shown you can use the bounding box in most cases, although it is not always guaranteed that the bounding box will fit neatly around the text. A box with a height of 50 and font size of 12 may not give the results you want depending on the UILabel configuration.

Query the UIString within the UILabel to determine its exact metrics and use these to better place your underline regardless of the enclosing bounding box or frame using the drawing code already provided by kovpas.

You should also look at UIFont's "leading" property that gives the distance between baselines based on a particular font. The baseline is where you would want your underline to be drawn.

Look up the UIKit additions to 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.

@semix 2010-04-26 08:19:49

Kenny it seems I can use the 3 methods to get the width of the 1st line of text easily, but how about the 2nd 3rd and other lines? Can you give an example?

@gnasher 2010-04-27 12:14:11

I have to concede. There is now way using NSString to achieve what you want, unless someone else has more to offer. I am going to have to suggest like the others before me to use UIWebView and stuff your text into the view: [webView loadHTMLString:@"<html><u>Underlined Text.</u></html>" baseURL:nil]; Let it do the layout and determination of where the lines should go. If it is a matter of you want the nth line underlined and you can't know which is the nth line, that is another matter.

Related Questions

Sponsored Content

14 Answered Questions

[SOLVED] Remove stubborn underline from link

48 Answered Questions

[SOLVED] Vertically align text to top within a UILabel

26 Answered Questions

[SOLVED] Multiple lines of text in UILabel

41 Answered Questions

[SOLVED] How do I find all files containing specific text on Linux?

2 Answered Questions

[SOLVED] Wrong bounding rect for right aligned text in UILabel

  • 2018-08-12 16:45:58
  • sashi_bhushan
  • 63 View
  • 0 Score
  • 2 Answer
  • Tags:   ios uilabel

5 Answered Questions

[SOLVED] Justify Text in UILabel iOS

  • 2011-08-22 10:17:25
  • Zaksh
  • 10986 View
  • 15 Score
  • 5 Answer
  • Tags:   iphone uilabel

3 Answered Questions

[SOLVED] Underline UILabel text

3 Answered Questions

[SOLVED] UILabel text include multiple spaces at the end

3 Answered Questions

Underline UIlabel so the underlining always reaches the bounds

  • 2015-12-09 10:16:58
  • DCDC
  • 93 View
  • -1 Score
  • 3 Answer
  • Tags:   ios swift uilabel

2 Answered Questions

Draw background color in a UILabel rect only where my text is

Sponsored Content