I have a UILabel which added in UIView and this view add as subview of UIScrollViewand zooming is enabled. Working every thing fine but when I zoom in then UILabel and other properties too and this look quite odd.I also added following code for flexible size.
userResizableView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
userResizableView.autoresizesSubviews = YES;
Above View is `UIView Type.
And below is code of Label
UILabel * lbl_FormFieldName = [[UILabel alloc]initWithFrame:CGRectMake(1, 1, rect.size.width, 15)];
lbl_FormFieldName.attributedText = attrText;
// lbl_FormFieldName.text = text;
lbl_FormFieldName.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
lbl_FormFieldName.textColor = [UIColor blackColor];
lbl_FormFieldName.backgroundColor = [UIColor clearColor];
lbl_FormFieldName.font = customFont;
lbl_FormFieldName.numberOfLines = 0;
lbl_FormFieldName.baselineAdjustment = UIBaselineAdjustmentAlignBaselines; // or UIBaselineAdjustmentAlignCenters, or UIBaselineAdjustmentNone
lbl_FormFieldName.clipsToBounds = YES;
lbl_FormFieldName.textAlignment = NSTextAlignmentCenter;
Kindly give me any suggestion how to make good. Looking for suggestion.
Thanks
Maybe the frame label is using floating numbers. Try this to force integers value for your frame:
[label setFrame:CGRectIntegral(label.frame)];
As per my requirement I have added pinch gesture on uilabel like this :
1. Add this line before implementation.
CG_INLINE CGRect CGRectScale(CGRect rect, CGFloat wScale, CGFloat hScale)
{
return CGRectMake(rect.origin.x * wScale, rect.origin.y * hScale, rect.size.width * wScale, rect.size.height * hScale);
}
2. Add gesture on label
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(viewDidPinch:)];
pinch.delegate =self;
[self addGestureRecognizer:pinch];
3. Add method in your file. Add scrollview bounds instead of self.bounds as I have different scenario in my case.
- (void)viewDidPinch:(UIPinchGestureRecognizer*)sender
{
CGFloat scale = sender.scale;
CGRect scaleRect = CGRectScale(self.bounds, scale, scale);
if (scaleRect.size.width >= 5 && scaleRect.size.height >= 5) {
[self._label setAttributedText:[self getScaledStringFrom:self._label.attributedText withScale:scale]];
[self._label setFrame:self.bounds];
}
sender.scale=1.0;
}
4. Rewrite text with new scale
- (NSMutableAttributedString*) getScaledStringFrom:(NSMutableAttributedString*)string withScale:(CGFloat)scale
{
[string beginEditing];
[string enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, string.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
if (value) {
UIFont *oldFont = (UIFont *)value;
UIFont *newFont = [oldFont fontWithSize:oldFont.pointSize * scale];
[string removeAttribute:NSFontAttributeName range:range];
[string addAttribute:NSFontAttributeName value:newFont range:range];
}
}];
[string endEditing];
return string;
}
I have a UILabel that I want to add two shadows.
A black colored shadow, and a white colored one.
One has -1 y-offset, and the other has 1 y-offset.
- (void)layoutSubviews{
[super layoutSubviews];
self.sectionTitleLabel.layer.shadowColor = [[UIColor whiteColor] CGColor];
self.sectionTitleLabel.layer.shadowRadius = 0.0f;
self.sectionTitleLabel.layer.shadowOpacity = .2;
self.sectionTitleLabel.layer.shadowOffset = CGSizeMake(0.f, -1.f);
CALayer *blackShadow = [[CALayer alloc] initWithLayer:self.sectionTitleLabel.layer];
blackShadow.shadowColor = [[UIColor blackColor] CGColor];
blackShadow.shadowRadius = 0.0f;
blackShadow.shadowOpacity = .4;
blackShadow.shadowOffset = CGSizeMake(0.f, 1.f);
[self.sectionTitleLabel.layer addSublayer:blackShadow];
self.sectionTitleLabel.layer.masksToBounds = NO;
}
With this the white shadow appears, but the black one does not.
I don't understand what do you mean by "two shadows to a UILabel" but I hope I can help. If on this picture you can see what you want, I will be happy :)
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableAttributedString* attString = [[NSMutableAttributedString alloc] initWithString:self.custLabel.text];
NSRange range = NSMakeRange(0, [attString length]);
[attString addAttribute:NSFontAttributeName value:self.custLabel.font range:range];
[attString addAttribute:NSForegroundColorAttributeName value:self.custLabel.textColor range:range];
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor whiteColor];
shadow.shadowOffset = CGSizeMake(0.0f, 1.0f);
[attString addAttribute:NSShadowAttributeName value:shadow range:range];
self.custLabel.attributedText = attString;
[self nextShadow];
}
-(void)nextShadow
{
self.custLabel.layer.masksToBounds = NO;
self.custLabel.layer.cornerRadius = 5;
self.custLabel.layer.shadowOffset = CGSizeMake(3, 0);
self.custLabel.layer.shadowRadius = 5;
self.custLabel.layer.shadowOpacity = 1.5;
}
I'd try to use
- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range;
But you can't use 2 shadow Attributes, only one. And you can customize
-(void)nextShadow
method to create good solution, for example
-(void)nextShadow
{
self.custLabel.layer.masksToBounds = NO;
self.custLabel.layer.cornerRadius = 1;
self.custLabel.layer.shadowOffset = CGSizeMake(1, 0);
self.custLabel.layer.shadowRadius = 1;
self.custLabel.layer.shadowOpacity = 1.5;
}
If you adjust values in -(void)nextShadow you can get what you want.
I'm building a PDF document dynamically in an iOS app. I am trying to convert an HTML formatted string to an NSAttributedString, then position it on my PDF document page.
If I treat the string as a plain NSString I can set it's frame and position it anywhere on the page (html tags included in the string).
If I create an NSAttributedString using initWithString I can also place the resulting attributed string anywhere on the page.
If I convert the string to an NSAttributedString using
NSAttributedString initWithData:options:documentAttributes:error:
the conversion work perfectly, and the tags are converted to attributes. But when I try to position the string's frame on the page the text is clipped as I move the frame down and right from 0,0. I can move the frame down 20 points before the text is clipped off the top, line by line. I can move the frame right until the origin is larger than the width, when all the text disappears from the page.
Code:
#pragma mark - === PDF BUILD Methods === -
- (void)buildPDF {
self.pdfPageSize = CGSizeMake(850.0, 1100.0);
UIFont *titleFont = [UIFont fontWithName:#"Dispatch-Bold" size:24.0];
UIFont *bodyFont = [UIFont systemFontOfSize:18.0];
// Open a new PDF dcument
[self setupPDFDocumentNamed:#"testPDF" Width:self.pdfPageSize.width Height:self.pdfPageSize.height];
//cover page
[self beginPDFPage];
NSString *title = [NSString stringWithFormat:#"How to Tie a %#", self.knot.knotName];
[self addText:title withFrame:CGRectMake(0, 10, 850, 30) font:titleFont alignment:NSTextAlignmentCenter];
[self addLineWithFrame:CGRectMake(10, 40, 830, 2) withColor:[CSCColors favoritesColor]];
NSString *imageName = [NSString stringWithFormat:#"%#_0", self.knot.knotID];
[self addImage:[UIImage imageNamed:imageName] atPoint:CGPointMake(50, 50)];
// addText method works, but addHTML method does not
//[self addText:self.knot.knotDescription withFrame:CGRectMake(400, 50, 300, 500) font:bodyFont alignment:NSTextAlignmentLeft];
[self addHTMLText:self.knot.knotDescription withFrame:CGRectMake(400, 50, 300, 500) font:bodyFont alignment:NSTextAlignmentLeft];
//close out the PDF
[self finishPDF];
}
-(void)setupPDFDocumentNamed:(NSString*)name Width:(float)width Height:(float)height {
self.pdfPageSize = CGSizeMake(width, height);
NSString *newPDFName = [NSString stringWithFormat:#"%#.pdf", name];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:newPDFName];
UIGraphicsBeginPDFContextToFile(pdfPath, CGRectZero, nil);
}
- (void)beginPDFPage {
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, self.pdfPageSize.width, self.pdfPageSize.height), nil);
}
- (void)finishPDF {
UIGraphicsEndPDFContext();
}
- (void)addText:(NSString*)text withFrame:(CGRect)frame font:(UIFont*)font alignment:(NSTextAlignment)alignment{
// Set up character and paragraph attributes
UIColor * cscBlueColor = [UIColor colorWithRed:0.0 green:0.53 blue:0.8 alpha:1.0];
NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
paraStyle.lineBreakMode = NSLineBreakByWordWrapping;
paraStyle.alignment = alignment;
//Calculate frame
NSDictionary *attr = #{NSFontAttributeName:font};
CGRect textRect = [text boundingRectWithSize:frame.size options:NSStringDrawingUsesLineFragmentOrigin attributes:attr context:nil];
CGSize stringSize = textRect.size;
float textWidth = frame.size.width;
if (textWidth < stringSize.width)
textWidth = stringSize.width;
if (textWidth > _pdfPageSize.width)
textWidth = _pdfPageSize.width - frame.origin.x;
CGRect renderingRect = CGRectMake(frame.origin.x, frame.origin.y, textWidth, stringSize.height);
NSDictionary *renderingAttr = #{NSParagraphStyleAttributeName:paraStyle, NSFontAttributeName:font, NSForegroundColorAttributeName:cscBlueColor};
[text drawInRect:renderingRect withAttributes:renderingAttr];
}
- (void)addHTMLText:(NSString*)text withFrame:(CGRect)frame font:(UIFont *)font alignment:(NSTextAlignment)alignment{
NSMutableAttributedString *myString = [[NSMutableAttributedString alloc] initWithData:[text dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
documentAttributes:nil
error:nil];
NSRange myRange;
myRange.location = 0;
myRange.length = myString.length;
// Set up character and paragraph attributes
UIColor * cscBlueColor = [UIColor colorWithRed:0.0 green:0.53 blue:0.8 alpha:1.0];
NSMutableParagraphStyle *paraStyle = [[NSMutableParagraphStyle alloc] init];
paraStyle.lineBreakMode = NSLineBreakByWordWrapping;
paraStyle.paragraphSpacing = 10.0f;
paraStyle.alignment = alignment;
NSDictionary *renderingAttr = #{NSParagraphStyleAttributeName:paraStyle, NSFontAttributeName:font, NSForegroundColorAttributeName:cscBlueColor};
[myString addAttributes:renderingAttr range:myRange];
//Calculate frame
NSStringDrawingContext *sdctx = [[NSStringDrawingContext alloc] init];
CGRect textRect = [myString boundingRectWithSize:CGSizeMake(frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:sdctx];
CGSize stringSize = textRect.size;
float textWidth = frame.size.width;
if (textWidth < stringSize.width)
textWidth = stringSize.width;
if (textWidth > _pdfPageSize.width)
textWidth = _pdfPageSize.width - frame.origin.x;
CGRect renderingRect = CGRectMake(frame.origin.x, frame.origin.y, textWidth, stringSize.height);
// fill rect so we can visualize the frame
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextFillRect(context, renderingRect);
//[myString drawInRect:renderingRect];
[myString drawWithRect:renderingRect options:NSStringDrawingUsesLineFragmentOrigin context:sdctx];
}
- (void)addImage:(UIImage*)image atPoint:(CGPoint)point {
CGRect imageFrame = CGRectMake(point.x, point.y, image.size.width, image.size.height);
[image drawInRect:imageFrame];
}
- (void)addLineWithFrame:(CGRect)frame withColor:(UIColor*)color {
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(currentContext, color.CGColor);
// this is the thickness of the line
CGContextSetLineWidth(currentContext, 2);
CGPoint startPoint = frame.origin;
CGPoint endPoint = CGPointMake(frame.origin.x + frame.size.width, frame.origin.y);
CGContextBeginPath(currentContext);
CGContextMoveToPoint(currentContext, startPoint.x, startPoint.y);
CGContextAddLineToPoint(currentContext, endPoint.x, endPoint.y);
CGContextClosePath(currentContext);
CGContextDrawPath(currentContext, kCGPathFillStroke);
}
Any ideas?
Screen caps:
For the 1st two screen caps, I used
NSAttributedString initWithData:options:documentAttributes:error:
to generate the attributed string
In the first, frame is set to {400, 50, 300, 500} and text is clipped completely.
In the second the frame is {400, 50, 401, 500}, the text appears, but the 1st two lines of text is clipped at the top. If I move the frame up to 20, all lines are visible.
In the final screen shot I just used initWithString to get the attributed string, and everything works as it should, except that the HTML is not converted to attributes.
I've also cleaned up the HTML string to make sure tht the tags and class calls were not introducing some wierd attributes. The cleaned up string has the same results:
<html><body><p>Landlubbers need not apply. The Sheet Bend is an essential knot to know and one of the first knots taught to new sailors. It is very fast to tie and is useful when joining two ropes of different diameters.</p><p>Also known as Becket bend (when made fast to an eye instead of a loop), the Sheet Bend is often considered one of the most essential knots and is related in structure to the bowline. If the two free ends are not on the same side of the knot, the result is a left-handed sheet bend of significantly reduced strength.</p></body></html>
Thanks
I am using Apple's Sample Code TheElements for this question. How would I go about drawing text outside of the elementSymbolRectangle. For example I would like to display the Elements Name, but I need it to be outside of the elementSymbolRectangle. I am new to programming and would appreciate any help.
Thanks
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setAutoresizesSubviews:YES];
[self setupUserInterface];
// set the background color of the view to clearn
self.backgroundColor=[UIColor clearColor];
}
return self;
}
- (void)jumpToWikipedia:(id)sender {
// create the string that points to the correct Wikipedia page for the element name
NSString *wikiPageString = [NSString stringWithFormat:#"http://en.wikipedia.org/wiki/%#", self.element.name];
if (![[UIApplication sharedApplication] openURL:[NSURL URLWithString:wikiPageString]])
{
// there was an error trying to open the URL. for the moment we'll simply ignore it.
}
}
- (void)drawRect:(CGRect)rect {
// get the background image for the state of the element
// position it appropriately and draw the image
//
UIImage *backgroundImage = [self.element stateImageForAtomicElementView];
CGRect elementSymbolRectangle = CGRectMake(0, 0, [backgroundImage size].width, [backgroundImage size].height);
[backgroundImage drawInRect:elementSymbolRectangle];
// all the text is drawn in white
[[UIColor whiteColor] set];
// draw the element number
UIFont *font = [UIFont boldSystemFontOfSize:32];
CGPoint point = CGPointMake(10,5);
[[NSString stringWithFormat:#"%#", self.element.atomicNumber] drawAtPoint:point withFont:font];
// draw the element symbol
CGSize stringSize = [self.element.symbol sizeWithFont:font];
point = CGPointMake((self.bounds.size.width-stringSize.width-10),5);
[self.element.symbol drawAtPoint:point withFont:font];
This solved my problem.
UILabel *scoreLabel = [ [UILabel alloc ] initWithFrame:CGRectMake((self.bounds.size.width / 2), -50.0, 100.0, 100.0) ];
scoreLabel.textAlignment = UITextAlignmentCenter;
scoreLabel.textColor = [UIColor whiteColor];
scoreLabel.backgroundColor = [UIColor blackColor];
scoreLabel.font = [UIFont fontWithName:#"Arial Rounded MT Bold" size:(36.0)];
[self addSubview:scoreLabel];
scoreLabel.text = [NSString stringWithFormat: #"Test"];
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.
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?
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.
In Swift:
let underlineAttriString = NSAttributedString(string: "attriString",
attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString
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];
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
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
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];
}
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;
}
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 ;-)
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;
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];
}
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;
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.
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
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];
}
Swift 4.1 ver:
let underlineAttriString = NSAttributedString(string:"attriString", attributes:
[NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])
label.attributedText = underlineAttriString
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.
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.
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
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];
}
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];
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])
}
}
}