在 UIlabel 中的文本下划线

如何在可能是多行字符串的文本下划线? 我发现有些人建议使用 UIWebView,但显然它对于文本渲染来说是一个太沉重的类。

我的想法是计算出每行中每个字符串的起始点和长度。 然后在下面画一条线。

我遇到了如何计算字符串的长度和起始点的问题。

我尝试使用 -[UILabel textRectForBounds:limitedToNumberOfLines:],这应该是文本的图纸边界直接吗? 然后我还得练习校准吗? 如何得到每条线的起始点,当它是中心对齐和右对齐?

130258 次浏览

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.

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.

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

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

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

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.

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

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

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

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

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

In Swift:

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

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

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];
}
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;

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;

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

Swift 4.1 ver:

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


label.attributedText = underlineAttriString

Sometimes we developer stuck in small designing part of any UI screen. One of the most irritating requirement is under line text. Don’t worry here is the solution.

enter image description here

Underlining a text in a UILabel using 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];

Underlining a text in UILabel using 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

You can use this my custom label! You can also use interface builder to set

import UIKit




class  YHYAttributedLabel : UILabel{
    

    

@IBInspectable
var underlineText : String = ""{
        

didSet{


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

        

}


}