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;
}
In a UIView occupying the entire screen I need to draw some hundred rectangles with text inside.
The UIView reacts to pan and pinch, moving and zooming the rectangles.
The text font changes according to the zooming.
I need a very fast way to draw the strings inside the rectangles. What I found so far is too slow.
I am add a test-example, the drawRect of a UIView, without zooming to be simple. It takes ~0.5 seconds on a iPad2 to draw the rectangles, which is very a long wait.
- (void)drawRect:(CGRect)rect
{
// Prepare the rectangles in which to draw the strings
// in the real app the size of the rectangles will change according to the zooming
// this takes very little time
CGRect rectangles[600] ;
float width = self.frame.size.width / 20.;
float height = self.frame.size.height / 30.;
int k = 0;
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 30; j++)
{
rectangles[k] = CGRectMake(i * width , j * height, width, height);
k++;
}
}
// In the real app, the font size will change according to the zooming
// it cannot be stored once for all
float txtSize = self.frame.size.width / 30. / 4;
NSDictionary *attributesTitle = #{NSFontAttributeName: [UIFont fontWithName:#"Verdana-bold" size:txtSize],
NSForegroundColorAttributeName: [UIColor blackColor]};
NSDictionary *attributes = #{NSFontAttributeName: [UIFont fontWithName:#"Verdana" size:txtSize],
NSForegroundColorAttributeName: [UIColor blackColor]};
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
[layoutManager addTextContainer:textContainer];
NSTextStorage *textStorage ;
NSRange glyphRange;
NSLog(#"Before");
for (int i = 0; i < 600; i++)
{
// In the real app, the font size will change according to the zooming, then the textStorage is not
// first string with title
textStorage = [ [NSTextStorage alloc] initWithString:[NSString stringWithFormat:#"A:%d", i] attributes:attributesTitle];
[textStorage addLayoutManager:layoutManager];
glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: rectangles[i].origin];
// second string with subtitle
textStorage = [ [NSTextStorage alloc] initWithString:[NSString stringWithFormat:#"B:%d", i] attributes:attributes];
[textStorage addLayoutManager:layoutManager];
glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: CGPointMake(rectangles[i].origin.x, rectangles[i].origin.y + 10)];
}
NSLog(#"After");
}
On Android I reached a better performance, but I am not so skilled in Objective-C
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])
}
}
}