Underline UIlabel so the underlining always reaches the bounds - ios

What I want to achieve is to have UILabel underlined but in a specific way.
I know how to make UILabel underlined, but since this is going to be a dynamic text, I don't know how long it will be.
Anytime the label enters a new line, I'd like to make the underlining align with the one above regardless of the text length.
I sketched it up to give you a better notion of what I actually try to achieve:
What is your opinion, how to approach such problem?
Should I add the white line as UIView anytime text skips to another line?
Or maybe add some whitespace in code when the text lengths is shorter than bounds of current line?

first you need to set text for you label then call this method :
- (void)underlineLabel:(UILabel*)lbl {
if (![lbl respondsToSelector:#selector(setAttributedText:)]) {
return;
}
NSMutableAttributedString *attributedText;
if (!lbl.attributedText) {
attributedText = [[NSMutableAttributedString alloc] initWithString:lbl.text];
} else {
attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:lbl.attributedText];
}
long len = [lbl.text length];
[attributedText addAttribute:NSUnderlineColorAttributeName value:[UIColor grayColor] range:NSMakeRange(0,len)];
[attributedText addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:1] range:NSMakeRange(0, len)];//Underline color
lbl.attributedText = attributedText;
}

func underlineLabel(label: UILabel) {
if !lbl.respondsToSelector("setAttributedText:") {
return
}
var attributedText = NSMutableAttributedString()
if !(lbl.attributedText != nil) {
attributedText = NSMutableAttributedString(string:label.text!)
}
else {
attributedText = NSMutableAttributedString(attributedString: label.attributedText!)
}
let str = label.text;
let len = str?.characters.count;
attributedText.addAttribute(NSUnderlineColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, len!))
attributedText.addAttribute(NSUnderlineStyleAttributeName , value:1, range: NSMakeRange(0, len!))
//Underline color
lbl.attributedText = attributedText
}

I have came up with solution with custom label class and override drawRect Method in that custom class of UIlabel.
CustomLabel.h
#import <UIKit/UIKit.h>
#interface CustomLabel : UILabel
#end
CustomLabel.m
#import "CustomLabel.h"
#implementation CustomLabel
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, 1.0f);
float Left = self.center.x - self.frame.size.width/2.0;
float Right = self.center.x + self.frame.size.width/2.0;
CGContextMoveToPoint(ctx, Left, self.bounds.size.height - 1);
CGContextAddLineToPoint(ctx, Right, self.bounds.size.height - 1);
CGContextStrokePath(ctx);
[super drawRect:rect];
}
#end
In Your Class Just import this custom Class.
#import "CustomLabel.h"
////// you can create labels now which are having underline to bounds.
-(void)CreateCustomLabel
{
CustomLabel *custom = [[CustomLabel alloc] initWithFrame:CGRectMake(0, 150, SCREEN_WIDTH-40, 50)];
custom.text = #"Your Text Here";
custom.backgroundColor = [UIColor redColor];
[self.view addSubview:custom];
}

Related

UITextView, NSAttributedString and custom attributes

I have searched a lot on Stack Overflow but I couldn't find a solution. Perhaps I just misinterpreted some answers.
I have created a UITextView and I am using NSAttributedStrings to work with the UITextView which is just fine.
Now, after adding a custom attribute, I am stuck.
Where can I hook in to render my custom attribute within the UITextView? Is there a delegate method, or will I have to create my own UITextView and overwrite a method?
You can custom NSLayoutManager, and implement it's -drawGlyphsForGlyphRange:atPoint: method.
For example, you want a custom background with a corner radius
textView init:
NSTextStorage *textStorage = [NSTextStorage new];
CustomLayoutManager *layoutManager = [[CustomLayoutManager alloc] init];
CGSize containerSize = CGSizeMake(self.view.bounds.size.width, CGFLOAT_MAX);
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:containerSize];
textContainer.widthTracksTextView = YES;
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
self.textView = [[UITextView alloc] initWithFrame:yourFrame textContainer:textContainer];
And apply your custom attribute:
NSMutableAttributedString *mAttrStr = [[NSMutableAttributedString alloc] initWithString:#"SampleText"];
[mAttrStr addAttribute:YourCustomAttributeName value:[UIColor redColor] range:NSMakeRange(0, mAttrStr.length)]; //for example, you want a custom background with a corner radius
[self.textView.textStorage appendAttributedString:mAttrStr];
In CustomLayoutManager.m
-(void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {
NSRange range = [self characterRangeForGlyphRange:glyphsToShow
actualGlyphRange:NULL];
//enumerate custom attribute in the range
[self.textStorage enumerateAttribute:YourCustomAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
if (value) {
UIColor *color = value; //the color set above
NSRange glyphRange = [self glyphRangeForCharacterRange:range
actualCharacterRange:NULL];
NSTextContainer *container = [self textContainerForGlyphAtIndex:glyphRange.location
effectiveRange:NULL];
//draw background
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, origin.x, origin.y);
[color setFill];
CGRect rect = [self boundingRectForGlyphRange:glyphRange inTextContainer:container];
//UIBezierPath with rounded
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:100];
[path fill];
CGContextRestoreGState(context);
//end draw
[super drawGlyphsForGlyphRange:range atPoint:origin];
}
else {
[super drawGlyphsForGlyphRange:range atPoint:origin];
}
}];
}
Now the 'SampleText' has a red rounded background.
Please refer this simple code snippet to set attributed string to a textview
let attributedString = NSMutableAttributedString(string:"Test string to add attributes")
attributedString.addAttributes([NSForegroundColorAttributeName:UIColor.greenColor()], range: NSMakeRange(0, attributedString.string.characters.count))
textView.attributedText = attributedString
For Objective-C
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:#"Test string to add attributes"];
[attributedString addAttributes:#{NSForegroundColorAttributeName:[UIColor greenColor]} range:NSMakeRange(0, attributedString.string.length)];
textView.attributedText = attributedString;
Hope this helps.
If you want apply particular attributes for particular textView rather then string then you should subclass UITextView and make custom initmethod or some method that return UITextView object with specified attribute!! You can pass custom attributes as parameter in method also if attributes are change i mean not fix. and if attribute will remain same implicitly then set attributes in that class by default.

Fit background color to special characters [duplicate]

I have a UILabel with space for two lines of text. Sometimes, when the text is too short, this text is displayed in the vertical center of the label.
How do I vertically align the text to always be at the top of the UILabel?
There's no way to set the vertical-align on a UILabel, but you can get the same effect by changing the label's frame. I've made my labels orange so you can see clearly what's happening.
Here's the quick and easy way to do this:
[myLabel sizeToFit];
If you have a label with longer text that will make more than one line, set numberOfLines to 0 (zero here means an unlimited number of lines).
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
Longer Version
I'll make my label in code so that you can see what's going on. You can set up most of this in Interface Builder too. My setup is a View-Based App with a background image I made in Photoshop to show margins (20 points). The label is an attractive orange color so you can see what's going on with the dimensions.
- (void)viewDidLoad
{
[super viewDidLoad];
// 20 point top and left margin. Sized to leave 20 pt at right.
CGRect labelFrame = CGRectMake(20, 20, 280, 150);
UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
[myLabel setBackgroundColor:[UIColor orangeColor]];
NSString *labelText = #"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
[myLabel setText:labelText];
// Tell the label to use an unlimited number of lines
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
[self.view addSubview:myLabel];
}
Some limitations of using sizeToFit come into play with center- or right-aligned text. Here's what happens:
// myLabel.textAlignment = NSTextAlignmentRight;
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
The label is still sized with a fixed top-left corner. You can save the original label's width in a variable and set it after sizeToFit, or give it a fixed width to counter these problems:
myLabel.textAlignment = NSTextAlignmentCenter;
[myLabel setNumberOfLines:0];
[myLabel sizeToFit];
CGRect myFrame = myLabel.frame;
// Resize the frame's width to 280 (320 - margins)
// width could also be myOriginalLabelFrame.size.width
myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
myLabel.frame = myFrame;
Note that sizeToFit will respect your initial label's minimum width. If you start with a label 100 wide and call sizeToFit on it, it will give you back a (possibly very tall) label with 100 (or a little less) width. You might want to set your label to the minimum width you want before resizing.
Some other things to note:
Whether lineBreakMode is respected depends on how it's set. NSLineBreakByTruncatingTail (the default) is ignored after sizeToFit, as are the other two truncation modes (head and middle). NSLineBreakByClipping is also ignored. NSLineBreakByCharWrapping works as usual. The frame width is still narrowed to fit to the rightmost letter.
Mark Amery gave a fix for NIBs and Storyboards using Auto Layout in the comments:
If your label is included in a nib or storyboard as a subview of the view of a ViewController that uses autolayout, then putting your sizeToFit call into viewDidLoad won't work, because autolayout sizes and positions the subviews after viewDidLoad is called and will immediately undo the effects of your sizeToFit call. However, calling sizeToFit from within viewDidLayoutSubviews will work.
My Original Answer (for posterity/reference):
This uses the NSString method sizeWithFont:constrainedToSize:lineBreakMode: to calculate the frame height needed to fit a string, then sets the origin and width.
Resize the frame for the label using the text you want to insert. That way you can accommodate any number of lines.
CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = #"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:#"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont
constrainedToSize:maximumSize
lineBreakMode:self.dateLabel.lineBreakMode];
CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);
self.dateLabel.frame = dateFrame;
Set the new text:
myLabel.text = #"Some Text"
Set the maximum number of lines to 0 (automatic):
myLabel.numberOfLines = 0
Set the frame of the label to the maximum size:
myLabel.frame = CGRectMake(20,20,200,800)
Call sizeToFit to reduce the frame size so the contents just fit:
[myLabel sizeToFit]
The labels frame is now just high and wide enough to fit your text. The top left should be unchanged. I have tested this only with the top left-aligned text. For other alignments, you might have to modify the frame afterward.
Also, my label has word wrapping enabled.
Refering to the extension solution:
for(int i=1; i< newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n"];
should be replaced by
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
Additional space is needed in every added newline, because iPhone UILabels' trailing carriage returns seems to be ignored :(
Similarly, alignBottom should be updated too with a #" \n#%" in place of "\n#%" (for cycle initialization must be replaced by "for(int i=0..." too).
The following extension works for me:
// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
// -- file: UILabel+VerticalAlign.m
#implementation UILabel (VerticalAlign)
- (void)alignTop {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [self.text stringByAppendingString:#"\n "];
}
- (void)alignBottom {
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<newLinesToPad; i++)
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
#end
Then call [yourLabel alignTop]; or [yourLabel alignBottom]; after each yourLabel text assignment.
Just in case it's of any help to anyone, I had the same problem but was able to solve the issue simply by switching from using UILabel to using UITextView. I appreciate this isn't for everyone because the functionality is a bit different.
If you do switch to using UITextView, you can turn off all the Scroll View properties as well as User Interaction Enabled... This will force it to act more like a label.
No muss, no fuss
#interface MFTopAlignedLabel : UILabel
#end
#implementation MFTopAlignedLabel
- (void)drawTextInRect:(CGRect) rect
{
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.text attributes:#{NSFontAttributeName:self.font}];
rect.size.height = [attributedText boundingRectWithSize:rect.size
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
if (self.numberOfLines != 0) {
rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
}
[super drawTextInRect:rect];
}
#end
No muss, no Objective-c, no fuss but Swift 3:
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Swift 4.2
class VerticalTopAlignLabel: UILabel {
override func drawText(in rect:CGRect) {
guard let labelText = text else { return super.drawText(in: rect) }
let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
var newRect = rect
newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height
if numberOfLines != 0 {
newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
}
super.drawText(in: newRect)
}
}
Easiest approach using Storyboard:
Embed Label in a StackView and set the following two attributes of StackView in the Attribute Inspector:
1- Axis to Horizontal,
2- Alignment to Top
Like the answer above, but it wasn't quite right, or easy to slap into code so I cleaned it up a bit. Add this extension either to it's own .h and .m file or just paste right above the implementation you intend to use it:
#pragma mark VerticalAlign
#interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
#end
#implementation UILabel (VerticalAlign)
- (void)alignTop
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i<= newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#" \n"];
}
}
- (void)alignBottom
{
CGSize fontSize = [self.text sizeWithFont:self.font];
double finalHeight = fontSize.height * self.numberOfLines;
double finalWidth = self.frame.size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i=0; i< newLinesToPad; i++)
{
self.text = [NSString stringWithFormat:#" \n%#",self.text];
}
}
#end
And then to use, put your text into the label, and then call the appropriate method to align it:
[myLabel alignTop];
or
[myLabel alignBottom];
An even quicker (and dirtier) way to accomplish this is by setting the UILabel's line break mode to "Clip" and adding a fixed amount of newlines.
myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];
This solution won't work for everyone -- in particular, if you still want to show "..." at the end of your string if it exceeds the number of lines you're showing, you'll need to use one of the longer bits of code -- but for a lot of cases this'll get you what you need.
Instead of UILabel you may use UITextField which has vertical alignment option:
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
I've struggled with this one for a long time and I wanted to share my solution.
This will give you a UILabel that will autoshrink text down to 0.5 scales and vertically center the text. These options are also available in Storyboard/IB.
[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
Create a new class
LabelTopAlign
.h file
#import <UIKit/UIKit.h>
#interface KwLabelTopAlign : UILabel {
}
#end
.m file
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect {
int lineHeight = [#"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
if(rect.size.height >= lineHeight) {
int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
int yMax = textHeight;
if (self.numberOfLines > 0) {
yMax = MIN(lineHeight*self.numberOfLines, yMax);
}
[super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
}
}
#end
Edit
Here's a simpler implementation that does the same:
#import "KwLabelTopAlign.h"
#implementation KwLabelTopAlign
- (void)drawTextInRect:(CGRect)rect
{
CGFloat height = [self.text sizeWithFont:self.font
constrainedToSize:rect.size
lineBreakMode:self.lineBreakMode].height;
if (self.numberOfLines != 0) {
height = MIN(height, self.font.lineHeight * self.numberOfLines);
}
rect.size.height = MIN(rect.size.height, height);
[super drawTextInRect:rect];
}
#end
In Interface Builder
Set UILabel to size of biggest possible Text
Set Lines to '0' in Attributes Inspector
In your code
Set the text of the label
Call sizeToFit on your label
Code Snippet:
self.myLabel.text = #"Short Title";
[self.myLabel sizeToFit];
For Adaptive UI(iOS8 or after) , Vertical Alignment of UILabel is to be set from StoryBoard by Changing the properties
noOfLines=0` and
Constraints
Adjusting UILabel LefMargin, RightMargin and Top Margin Constraints.
Change Content Compression Resistance Priority For Vertical=1000` So that Vertical>Horizontal .
Edited:
noOfLines=0
and the following constraints are enough to achieve the desired results.
Create a subclass of UILabel. Works like a charm:
// TopLeftLabel.h
#import <Foundation/Foundation.h>
#interface TopLeftLabel : UILabel
{
}
#end
// TopLeftLabel.m
#import "TopLeftLabel.h"
#implementation TopLeftLabel
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
textRect.origin.y = bounds.origin.y;
return textRect;
}
-(void)drawTextInRect:(CGRect)requestedRect
{
CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
}
#end
As discussed here.
What I did in my app was to set the UILabel's line property to 0 as well as to create a bottom constraint of the UILabel and make sure it is being set to >= 0 as shown in the image below.
Use textRect(forBounds:limitedToNumberOfLines:).
class TopAlignedLabel: UILabel {
override func drawText(in rect: CGRect) {
let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
super.drawText(in: textRect)
}
}
I wrote a util function to achieve this purpose. You can take a look:
// adjust the height of a multi-line label to make it align vertical with top
+ (void) alignLabelWithTop:(UILabel *)label {
CGSize maxSize = CGSizeMake(label.frame.size.width, 999);
label.adjustsFontSizeToFitWidth = NO;
// get actual height
CGSize actualSize = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
CGRect rect = label.frame;
rect.size.height = actualSize.height;
label.frame = rect;
}
.How to use? (If lblHello is created by Interface builder, so I skip some UILabel attributes detail)
lblHello.text = #"Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop:lblHello];
I also wrote it on my blog as an article:
http://fstoke.me/blog/?p=2819
I took a while to read the code, as well as the code in the introduced page, and found that they all try to modify the frame size of label, so that the default center vertical alignment would not appear.
However, in some cases we do want the label to occupy all those spaces, even if the label does have so much text (e.g. multiple rows with equal height).
Here, I used an alternative way to solve it, by simply pad newlines to the end of label (pls note that I actually inherited the UILabel, but it is not necessary):
CGSize fontSize = [self.text sizeWithFont:self.font];
finalHeight = fontSize.height * self.numberOfLines;
finalWidth = size.width; //expected width of label
CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
int newLinesToPad = (finalHeight - theStringSize.height) / fontSize.height;
for(int i = 0; i < newLinesToPad; i++)
{
self.text = [self.text stringByAppendingString:#"\n "];
}
I took the suggestions here and created a view which can wrap a UILabel and will size it and set the number of lines so that it is top aligned. Simply put a UILabel as a subview:
#interface TopAlignedLabelContainer : UIView
{
}
#end
#implementation TopAlignedLabelContainer
- (void)layoutSubviews
{
CGRect bounds = self.bounds;
for (UILabel *label in [self subviews])
{
if ([label isKindOfClass:[UILabel class]])
{
CGSize fontSize = [label.text sizeWithFont:label.font];
CGSize textSize = [label.text sizeWithFont:label.font
constrainedToSize:bounds.size
lineBreakMode:label.lineBreakMode];
label.numberOfLines = textSize.height / fontSize.height;
label.frame = CGRectMake(0, 0, textSize.width,
fontSize.height * label.numberOfLines);
}
}
}
#end
You can use TTTAttributedLabel, it supports vertical alignment.
#property (nonatomic) TTTAttributedLabel* label;
<...>
//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
I've found the answers on this question are now a bit out-of-date, so adding this for the auto layout fans out there.
Auto layout makes this issue pretty trivial. Assuming we're adding the label to UIView *view, the following code will accomplish this:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:#"Some text here"];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:label];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[label]|" options:0 metrics:nil views:#{#"label": label}]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label]" options:0 metrics:nil views:#{#"label": label}]];
The label's height will be calculated automatically (using it's intrinsicContentSize) and the label will be positioned edge-to-edge horizontally, at the top of the view.
I've used a lot of the methods above, and just want to add a quick-and-dirty approach I've used:
myLabel.text = [NSString stringWithFormat:#"%#\n\n\n\n\n\n\n\n\n",#"My label text string"];
Make sure the number of newlines in the string will cause any text to fill the available vertical space, and set the UILabel to truncate any overflowing text.
Because sometimes good enough is good enough.
I wanted to have a label which was able to have multi-lines, a minimum font size, and centred both horizontally and vertically in it's parent view. I added my label programmatically to my view:
- (void) customInit {
// Setup label
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.label.numberOfLines = 0;
self.label.lineBreakMode = UILineBreakModeWordWrap;
self.label.textAlignment = UITextAlignmentCenter;
// Add the label as a subview
self.autoresizesSubviews = YES;
[self addSubview:self.label];
}
And then when I wanted to change the text of my label...
- (void) updateDisplay:(NSString *)text {
if (![text isEqualToString:self.label.text]) {
// Calculate the font size to use (save to label's font)
CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
}
// In cases where the frame is still too large (when we're exceeding minimum font size),
// use the views size
if (textSize.height > self.frame.size.height) {
textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
}
// Draw
self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
self.label.text = text;
}
[self setNeedsDisplay];
}
Hope that helps someone!
FXLabel (on github) does this out of the box by setting label.contentMode to UIViewContentModeTop. This component is not made by me, but it is a component I use frequently and has tons of features, and seems to work well.
for anyone reading this because the text inside your label is not vertically centered, keep in mind that some font types are not designed equally. for example, if you create a label with zapfino size 16, you will see the text is not perfectly centered vertically.
however, working with helvetica will vertically center your text.
Subclass UILabel and constrain the drawing rectangle, like this:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
[super drawTextInRect:rect];
}
I tried the solution involving newline padding and ran into incorrect behavior in some cases. In my experience, it's easier to constrain the drawing rect as above than mess with numberOfLines.
P.S. You can imagine easily supporting UIViewContentMode this way:
- (void)drawTextInRect:(CGRect)rect
{
CGSize sizeThatFits = [self sizeThatFits:rect.size];
if (self.contentMode == UIViewContentModeTop) {
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
else if (self.contentMode == UIViewContentModeBottom) {
rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
rect.size.height = MIN(rect.size.height, sizeThatFits.height);
}
[super drawTextInRect:rect];
}
If you are using autolayout, set the vertical contentHuggingPriority to 1000, either in code or IB. In IB you may then have to remove a height constraint by setting it's priority to 1 and then deleting it.
As long as you are not doing any complex task, you can use UITextView instead of UILabels.
Disable the scroll.
If you want the text to be displayed completely just user sizeToFit and sizeThatFits: methods
In swift,
let myLabel : UILabel!
To make your text of your Label to fit to screen and it's on the top
myLabel.sizeToFit()
To make your font of label to fit to the width of screen or specific width size.
myLabel.adjustsFontSizeToFitWidth = YES
and some textAlignment for label :
myLabel.textAlignment = .center
myLabel.textAlignment = .left
myLabel.textAlignment = .right
myLabel.textAlignment = .Natural
myLabel.textAlignment = .Justified
This is an old solution, use the autolayout on iOS >= 6
My solution:
Split lines by myself (ignoring label wrap settings)
Draw lines by myself (ignoring label alignment)
#interface UITopAlignedLabel : UILabel
#end
#implementation UITopAlignedLabel
#pragma mark Instance methods
- (NSArray*)splitTextToLines:(NSUInteger)maxLines {
float width = self.frame.size.width;
NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSMutableArray* lines = [NSMutableArray array];
NSMutableString* buffer = [NSMutableString string];
NSMutableString* currentLine = [NSMutableString string];
for (NSString* word in words) {
if ([buffer length] > 0) {
[buffer appendString:#" "];
}
[buffer appendString:word];
if (maxLines > 0 && [lines count] == maxLines - 1) {
[currentLine setString:buffer];
continue;
}
float bufferWidth = [buffer sizeWithFont:self.font].width;
if (bufferWidth < width) {
[currentLine setString:buffer];
}
else {
[lines addObject:[NSString stringWithString:currentLine]];
[buffer setString:word];
[currentLine setString:buffer];
}
}
if ([currentLine length] > 0) {
[lines addObject:[NSString stringWithString:currentLine]];
}
return lines;
}
- (void)drawRect:(CGRect)rect {
if ([self.text length] == 0) {
return;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.textColor.CGColor);
CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);
NSArray* lines = [self splitTextToLines:self.numberOfLines];
NSUInteger numLines = [lines count];
CGSize size = self.frame.size;
CGPoint origin = CGPointMake(0.0f, 0.0f);
for (NSUInteger i = 0; i < numLines; i++) {
NSString* line = [lines objectAtIndex:i];
if (i == numLines - 1) {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];
}
else {
[line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
}
origin.y += self.font.lineHeight;
if (origin.y >= size.height) {
return;
}
}
}
#end

Highlight just the text in a UILabel

I'm attempting to set the background color/highlight just the text within a UILabel. The issue is that the line breaks and spaces added to the UILabel to keep the text centered are also being highlighted.
Notice the spacing before the last line in the UILabel is highlighted. Also, the beginning and end of any new lines are also highlighted.
I'm creating the example above with the following code:
-(void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40,
self.view.frame.size.height - 300)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
someLabel.textAlignment = NSTextAlignmentCenter;
someLabel.textColor = [UIColor whiteColor];
someLabel.lineBreakMode = NSLineBreakByWordWrapping;
someLabel.numberOfLines = 0;
[self.view addSubview:someLabel];
// This string will be different lengths all the time
NSString *someLongString = #"Here is a really long amount of text that is going to wordwrap/line break and I don't want to highlight the spacing. I want to just highlight the words and a single space before/after the word";
// Create attributed string
NSMutableAttributedString *someLongStringAttr=[[NSMutableAttributedString alloc] initWithString:someLongString attributes:nil];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}
The output I'd like to achieve is to highlight only the text and the spaces between the words if there is only one space. The length of the text and the size of the UILabel will constantly be different so hard coding a solution is not an option unfortunately.
It seemed to me that the line break was the problem.
My idea was to try and know when the UILabel would add a line break and then just remove that character from the range of characters being highlighted.
It appears that you can't just ask UILabel where the line breaks are going to be but you can check what the size of an NSString will be when you add it to a label.
Using this information you can increment through each character constantly checking the height, and when the height changes you know you have a new line.
I have made an example that takes the Label's string and separates it into its individual lines that will appear in the UILabel. Once I have each line, I just set the background color on each line instead of the whole string. This eliminates and background colors being set on line breaks.
There are probably better solutions, and this one could probably be optimized for better performance, but it's a starting point and it appears to work.
- (void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40,
self.view.frame.size.height - 300)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
someLabel.textAlignment = NSTextAlignmentCenter;
someLabel.textColor = [UIColor whiteColor];
someLabel.lineBreakMode = NSLineBreakByWordWrapping;
someLabel.numberOfLines = 0;
[self.view addSubview:someLabel];
// This string will be different lengths all the time
NSString *someLongString = #"Here is a really long amount of text that is going to wordwrap/line break and I don't want to highlight the spacing. I want to just highlight the words and a single space before/after the word";
// Create attributed string
NSMutableAttributedString *someLongStringAttr=[[NSMutableAttributedString alloc] initWithString:someLongString attributes:nil];
// The idea here is to figure out where the UILabel would automatically make a line break and get each line of text separately.
// Temporarily set the label to be that string so that we can guess where the UILabel naturally puts its line breaks.
[someLabel setText:someLongString];
// Get an array of each individual line as the UILabel would present it.
NSArray *allLines = getLinesForLabel(someLabel);
[someLabel setText:#""];
// Loop through each line of text and apply the background color to just the text within that range.
// This way, no whitespace / line breaks will be highlighted.
__block int startRange = 0;
[allLines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL *stop) {
// The end range should be the length of the line, minus one for the whitespace.
// If we are on the final line, there are no more line breaks so we use the whole line length.
NSUInteger endRange = (idx+1 == allLines.count) ? line.length : line.length-1;
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(startRange, endRange)];
// Update the start range to the next line
startRange += line.length;
}];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}
#pragma mark - Utility Functions
static NSArray *getLinesForLabel(UILabel *label) {
// Get the text from the label
NSString *labelText = label.text;
// Create an array to hold the lines of text
NSMutableArray *allLines = [NSMutableArray array];
while (YES) {
// Get the length of the current line of text
int length = getLengthOfTextInFrame(label, labelText) + 1;
// Add this line of text to the array
[allLines addObject:[labelText substringToIndex:length]];
// Adjust the label text
labelText = [labelText substringFromIndex:length];
// Check for the final line
if(labelText.length<length) {
[allLines addObject:labelText];
break;
}
}
return [NSArray arrayWithArray:allLines];
}
static int getLengthOfTextInFrame(UILabel *label, NSString *text) {
// Create a block for getting the bounds of the current peice of text.
CGRect (^boundingRectForLength)(int) = ^CGRect(int length) {
NSString *cutText = [text substringToIndex:length];
CGRect textRect = [cutText boundingRectWithSize:CGSizeMake(label.frame.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName : label.font}
context:nil];
return textRect;
};
// Get the frame of the string for one character
int length = 1;
int lastSpace = 1;
CGRect textRect = boundingRectForLength(length);
CGFloat oneLineHeight = CGRectGetHeight(textRect);
// Keep adding one character to the string until the height changes, then you know you have a new line
while (textRect.size.height <= oneLineHeight)
{
// If the next character is white space, save the current length.
// It could be the end of the line.
// This will not work for character wrap.
if ([[text substringWithRange:NSMakeRange (length, 1)] isEqualToString:#" "]) {
lastSpace = length;
}
// Increment length and get the new bounds
textRect = boundingRectForLength(++length);
}
return lastSpace;
}
I've faced same issue and found out easier solution without huge performance costs. You can simply add TTTAttributedLabel to your project.
My demo project for the question:
#import "TTTAttributedLabel.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *label1 = [UILabel new];
label1.textAlignment = NSTextAlignmentCenter;
label1.numberOfLines = 0;
label1.frame = CGRectMake(20, 0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label1];
TTTAttributedLabel *label2 = [TTTAttributedLabel new];
label2.textAlignment = NSTextAlignmentCenter;
label2.numberOfLines = 0;
label2.frame = CGRectMake(20, CGRectGetHeight(self.view.frame) / 2.0, CGRectGetWidth(self.view.frame) - 40, CGRectGetHeight(self.view.frame) / 2.0);
[self.view addSubview:label2];
NSDictionary *attributes = #{NSBackgroundColorAttributeName:[UIColor blackColor], NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:32 weight:UIFontWeightBold]};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:#"Some very long string which can contain newlines and some other stuff" attributes:attributes];
label1.attributedText = string;
label2.text = string;
}
#end
Starting at iOS 10.3 the same code in question now produces the desired result. Not sure if this is a bug or a new feature.
-(void)createSomeLabel {
// Create and position my label
UILabel *someLabel = [[UILabel alloc] initWithFrame:CGRectMake(0,
0,
self.view.frame.size.width - 40.0,
self.view.frame.size.height - 300.0)];
someLabel.center = CGPointMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0);
someLabel.textAlignment = NSTextAlignmentCenter;
someLabel.textColor = [UIColor whiteColor];
someLabel.lineBreakMode = NSLineBreakByWordWrapping;
someLabel.numberOfLines = 0;
[self.view addSubview:someLabel];
// This string will be different lengths all the time
NSString *someLongString = #"Here is a really long amount of text that is going to wordwrap/line break and I don't want to highlight the spacing. I want to just highlight the words and a single space before/after the word";
// Create attributed string
NSMutableAttributedString *someLongStringAttr = [[NSMutableAttributedString alloc] initWithString:someLongString attributes:nil];
// Apply background color
[someLongStringAttr addAttribute:NSBackgroundColorAttributeName
value:[UIColor colorWithWhite:0 alpha:0.25]
range:NSMakeRange(0, someLongStringAttr.length)];
// Set text of label
someLabel.attributedText = someLongStringAttr;
}

Underline text in UIlabel

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

iPhone UITextField - Change placeholder text color

I'd like to change the color of the placeholder text I set in my UITextField controls, to make it black.
I'd prefer to do this without using normal text as the placeholder and having to override all the methods to imitate the behaviour of a placeholder.
I believe if I override this method:
- (void)drawPlaceholderInRect:(CGRect)rect
then I should be able to do this. But I'm unsure how to access the actual placeholder object from within this method.
Since the introduction of attributed strings in UIViews in iOS 6, it's possible to assign a color to the placeholder text like this:
if ([textField respondsToSelector:#selector(setAttributedPlaceholder:)]) {
UIColor *color = [UIColor blackColor];
textField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholderText attributes:#{NSForegroundColorAttributeName: color}];
} else {
NSLog(#"Cannot set placeholder text's color, because deployment target is earlier than iOS 6.0");
// TODO: Add fall-back code to set placeholder color.
}
Easy and pain-free, could be an easy alternative for some.
_placeholderLabel.textColor
Not suggested for production, Apple may reject your submission.
You can override drawPlaceholderInRect:(CGRect)rect as such to manually render the placeholder text:
- (void) drawPlaceholderInRect:(CGRect)rect {
[[UIColor blueColor] setFill];
[[self placeholder] drawInRect:rect withFont:[UIFont systemFontOfSize:16]];
}
This works in Swift <3.0:
myTextField.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSForegroundColorAttributeName : UIColor.redColor()])
Tested in iOS 8.2 and iOS 8.3 beta 4.
Swift 3:
myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSForegroundColorAttributeName : UIColor.red])
Swift 4:
myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSAttributedStringKey.foregroundColor: UIColor.red])
Swift 4.2:
myTextfield.attributedPlaceholder =
NSAttributedString(string: "placeholder text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])
You can Change the Placeholder textcolor to any color which you want by using the below code.
UIColor *color = [UIColor lightTextColor];
YOURTEXTFIELD.attributedPlaceholder = [[NSAttributedString alloc] initWithString:#"PlaceHolder Text" attributes:#{NSForegroundColorAttributeName: color}];
Maybe you want to try this way, but Apple might warn you about accessing private ivar:
[self.myTextField setValue:[UIColor darkGrayColor]
forKeyPath:#"_placeholderLabel.textColor"];
NOTE
This is not working on iOS 7 anymore, according to Martin Alléus.
Swift 3.0 + Storyboard
In order to change placeholder color in storyboard, create an extension with next code. (feel free to update this code, if you think, it can be clearer and safer).
extension UITextField {
#IBInspectable var placeholderColor: UIColor {
get {
guard let currentAttributedPlaceholderColor = attributedPlaceholder?.attribute(NSForegroundColorAttributeName, at: 0, effectiveRange: nil) as? UIColor else { return UIColor.clear }
return currentAttributedPlaceholderColor
}
set {
guard let currentAttributedString = attributedPlaceholder else { return }
let attributes = [NSForegroundColorAttributeName : newValue]
attributedPlaceholder = NSAttributedString(string: currentAttributedString.string, attributes: attributes)
}
}
}
Swift 4 version
extension UITextField {
#IBInspectable var placeholderColor: UIColor {
get {
return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
}
set {
guard let attributedPlaceholder = attributedPlaceholder else { return }
let attributes: [NSAttributedStringKey: UIColor] = [.foregroundColor: newValue]
self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
}
}
}
Swift 5 version
extension UITextField {
#IBInspectable var placeholderColor: UIColor {
get {
return attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor ?? .clear
}
set {
guard let attributedPlaceholder = attributedPlaceholder else { return }
let attributes: [NSAttributedString.Key: UIColor] = [.foregroundColor: newValue]
self.attributedPlaceholder = NSAttributedString(string: attributedPlaceholder.string, attributes: attributes)
}
}
}
In Swift:
if let placeholder = yourTextField.placeholder {
yourTextField.attributedPlaceholder = NSAttributedString(string:placeholder,
attributes: [NSForegroundColorAttributeName: UIColor.blackColor()])
}
In Swift 4.0:
if let placeholder = yourTextField.placeholder {
yourTextField.attributedPlaceholder = NSAttributedString(string:placeholder,
attributes: [NSAttributedStringKey.foregroundColor: UIColor.black])
}
The following only with iOS6+ (as indicated in Alexander W's comment):
UIColor *color = [UIColor grayColor];
nameText.attributedPlaceholder =
[[NSAttributedString alloc]
initWithString:#"Full Name"
attributes:#{NSForegroundColorAttributeName:color}];
I had already faced this issue. In my case below code is correct.
Objective C
[textField setValue:[UIColor whiteColor] forKeyPath:#"_placeholderLabel.textColor"];
For Swift 4.X
tf_mobile.setValue(UIColor.white, forKeyPath: "_placeholderLabel.textColor")
For iOS 13 Swift Code
tf_mobile.attributedPlaceholder = NSAttributedString(string:"PlaceHolder Text", attributes: [NSAttributedString.Key.foregroundColor: UIColor.red])
You can also use below code for iOS 13
let iVar = class_getInstanceVariable(UITextField.self, "_placeholderLabel")!
let placeholderLabel = object_getIvar(tf_mobile, iVar) as! UILabel
placeholderLabel.textColor = .red
Hope, this may help you.
With this we can change the color of textfield's placeholder text in iOS
[self.userNameTxt setValue:[UIColor colorWithRed:41.0/255.0 green:91.0/255.0 blue:106.0/255.0 alpha:1.0] forKeyPath:#"_placeholderLabel.textColor"];
in swift 3.X
textField.attributedPlaceholder = NSAttributedString(string: "placeholder text", attributes:[NSForegroundColorAttributeName: UIColor.black])
in swift 5
textField.attributedPlaceholder = NSAttributedString(string: "placeholder text", attributes: [NSAttributedString.Key.foregroundColor : UIColor.black])
Why don't you just use UIAppearance method:
[[UILabel appearanceWhenContainedIn:[UITextField class], nil] setTextColor:[UIColor whateverColorYouNeed]];
Also in your storyboard, without single line of code
For iOS 6.0 +
[textfield setValue:your_color forKeyPath:#"_placeholderLabel.textColor"];
Hope it helps.
Note: Apple may reject (0.01% chances) your app as we are accessing private API. I am using this in all my projects since two years, but Apple didn't ask for this.
For Xamarin.iOS developers, I found it from this document
https://developer.xamarin.com/api/type/Foundation.NSAttributedString/
textField.AttributedPlaceholder = new NSAttributedString ("Hello, world",new UIStringAttributes () { ForegroundColor = UIColor.Red });
Swift version. Probably it would help someone.
class TextField: UITextField {
override var placeholder: String? {
didSet {
let placeholderString = NSAttributedString(string: placeholder!, attributes: [NSForegroundColorAttributeName: UIColor.whiteColor()])
self.attributedPlaceholder = placeholderString
}
}
}
iOS 6 and later offers attributedPlaceholder on UITextField.
iOS 3.2 and later offers setAttributes:range: on NSMutableAttributedString.
You can do the following:
NSMutableAttributedString *ms = [[NSMutableAttributedString alloc] initWithString:self.yourInput.placeholder];
UIFont *placeholderFont = self.yourInput.font;
NSRange fullRange = NSMakeRange(0, ms.length);
NSDictionary *newProps = #{NSForegroundColorAttributeName:[UIColor yourColor], NSFontAttributeName:placeholderFont};
[ms setAttributes:newProps range:fullRange];
self.yourInput.attributedPlaceholder = ms;
To handle both vertical and horizontal alignment as well as color of placeholder in iOS7. drawInRect and drawAtPoint no longer use current context fillColor.
https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/CustomTextProcessing/CustomTextProcessing.html
Obj-C
#interface CustomPlaceHolderTextColorTextField : UITextField
#end
#implementation CustomPlaceHolderTextColorTextField : UITextField
-(void) drawPlaceholderInRect:(CGRect)rect {
if (self.placeholder) {
// color of placeholder text
UIColor *placeHolderTextColor = [UIColor redColor];
CGSize drawSize = [self.placeholder sizeWithAttributes:[NSDictionary dictionaryWithObject:self.font forKey:NSFontAttributeName]];
CGRect drawRect = rect;
// verticially align text
drawRect.origin.y = (rect.size.height - drawSize.height) * 0.5;
// set alignment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = self.textAlignment;
// dictionary of attributes, font, paragraphstyle, and color
NSDictionary *drawAttributes = #{NSFontAttributeName: self.font,
NSParagraphStyleAttributeName : paragraphStyle,
NSForegroundColorAttributeName : placeHolderTextColor};
// draw
[self.placeholder drawInRect:drawRect withAttributes:drawAttributes];
}
}
#end
This solution for Swift 4.1
textName.attributedPlaceholder = NSAttributedString(string: textName.placeholder!, attributes: [NSAttributedStringKey.foregroundColor : UIColor.red])
Categories FTW. Could be optimized to check for effective color change.
#import <UIKit/UIKit.h>
#interface UITextField (OPConvenience)
#property (strong, nonatomic) UIColor* placeholderColor;
#end
#import "UITextField+OPConvenience.h"
#implementation UITextField (OPConvenience)
- (void) setPlaceholderColor: (UIColor*) color {
if (color) {
NSMutableAttributedString* attrString = [self.attributedPlaceholder mutableCopy];
[attrString setAttributes: #{NSForegroundColorAttributeName: color} range: NSMakeRange(0, attrString.length)];
self.attributedPlaceholder = attrString;
}
}
- (UIColor*) placeholderColor {
return [self.attributedPlaceholder attribute: NSForegroundColorAttributeName atIndex: 0 effectiveRange: NULL];
}
#end
Swift 5 WITH CAVEAT.
let attributes = [ NSAttributedString.Key.foregroundColor: UIColor.someColor ]
let placeHolderString = NSAttributedString(string: "DON'T_DELETE", attributes: attributes)
txtField.attributedPlaceholder = placeHolderString
The caveat being that you MUST enter a non-empty String where "DON'T_DELETE" is, even if that string is set in code elsewhere. Might save you five minutes of head-sctratching.
if subclassing you MUST do it in layoutSubviews (not in init)
strangely you do NOT have to clear the normal placeholder. it knows to not draw placeholder if you're using the attributed placeholder.
Overriding drawPlaceholderInRect: would be the correct way, but it does not work due to a bug in the API (or the documentation).
The method never gets called on an UITextField.
See also drawTextInRect on UITextField not called
You might use digdog's solution. As I am not sure if that gets past Apples review, I chose a different solution: Overlay the text field with my own label which imitates the placeholder behaviour.
This is a bit messy though.
The code looks like this (Note I am doing this inside a subclass of TextField):
#implementation PlaceholderChangingTextField
- (void) changePlaceholderColor:(UIColor*)color
{
// Need to place the overlay placeholder exactly above the original placeholder
UILabel *overlayPlaceholderLabel = [[[UILabel alloc] initWithFrame:CGRectMake(self.frame.origin.x + 8, self.frame.origin.y + 4, self.frame.size.width - 16, self.frame.size.height - 8)] autorelease];
overlayPlaceholderLabel.backgroundColor = [UIColor whiteColor];
overlayPlaceholderLabel.opaque = YES;
overlayPlaceholderLabel.text = self.placeholder;
overlayPlaceholderLabel.textColor = color;
overlayPlaceholderLabel.font = self.font;
// Need to add it to the superview, as otherwise we cannot overlay the buildin text label.
[self.superview addSubview:overlayPlaceholderLabel];
self.placeholder = nil;
}
Iam new to xcode and i found a way around to the same effect.
I placed a uilabel in place of place holder with the desired format and hide it in
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
switch (textField.tag)
{
case 0:
lblUserName.hidden=YES;
break;
case 1:
lblPassword.hidden=YES;
break;
default:
break;
}
}
I agree its a work around and not a real solution but the effect was same got it from this link
NOTE: Still works on iOS 7 :|
The best i can do for both iOS7 and less is:
- (CGRect)placeholderRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
return [self textRectForBounds:bounds];
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect rect = CGRectInset(bounds, 0, 6); //TODO: can be improved by comparing font size versus bounds.size.height
return rect;
}
- (void)drawPlaceholderInRect:(CGRect)rect {
UIColor *color =RGBColor(65, 65, 65);
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[self.placeholder drawInRect:rect withAttributes:#{NSFontAttributeName:self.font, UITextAttributeTextColor:color}];
} else {
[color setFill];
[self.placeholder drawInRect:rect withFont:self.font];
}
}
For those using Monotouch (Xamarin.iOS), here's Adam's answer, translated to C#:
public class MyTextBox : UITextField
{
public override void DrawPlaceholder(RectangleF rect)
{
UIColor.FromWhiteAlpha(0.5f, 1f).SetFill();
new NSString(this.Placeholder).DrawString(rect, Font);
}
}
For set Attributed Textfield Placeholder with Multiple color ,
Just specify the Text ,
//txtServiceText is your Textfield
_txtServiceText.placeholder=#"Badal/ Shah";
NSMutableAttributedString *mutable = [[NSMutableAttributedString alloc] initWithString:_txtServiceText.placeholder];
[mutable addAttribute: NSForegroundColorAttributeName value:[UIColor whiteColor] range:[_txtServiceText.placeholder rangeOfString:#"Badal/"]]; //Replace it with your first color Text
[mutable addAttribute: NSForegroundColorAttributeName value:[UIColor orangeColor] range:[_txtServiceText.placeholder rangeOfString:#"Shah"]]; // Replace it with your secondcolor string.
_txtServiceText.attributedPlaceholder=mutable;
Output :-
I needed to keep the placeholder alignment so adam's answer was not enough for me.
To solve this I used a small variation that I hope will help some of you too:
- (void) drawPlaceholderInRect:(CGRect)rect {
//search field placeholder color
UIColor* color = [UIColor whiteColor];
[color setFill];
[self.placeholder drawInRect:rect withFont:self.font lineBreakMode:UILineBreakModeTailTruncation alignment:self.textAlignment];
}
[txt_field setValue:ColorFromHEX(#"#525252") forKeyPath:#"_placeholderLabel.textColor"];
Another option that doesn't require subclassing - leave placeholder blank, and put a label on top of edit button. Manage the label just like you would manage the placeholder (clearing once user inputs anything..)

Resources