Make link in UILabel.attributedText *not* blue and *not* underlined - ios
I want some words within my OHAttributedLabel to be links, but I want them to be colors other than blue and I don't want the underline.
This is giving me a blue link with underlined text:
-(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{
NSMutableAttributedString* mutableAttributedText = [self.label.attributedText mutableCopy];
[mutableAttributedText beginEditing];
[mutableAttributedText addAttribute:kOHLinkAttributeName
value:[NSURL URLWithString:#"http://www.somewhere.net"]
range:range];
[mutableAttributedText addAttribute:(id)kCTForegroundColorAttributeName
value:color
range:range];
[mutableAttributedText addAttribute:(id)kCTUnderlineStyleAttributeName
value:[NSNumber numberWithInt:kCTUnderlineStyleNone]
range:range];
[mutableAttributedText endEditing];
self.label.attributedText = mutableAttributedText;
}
Since I'm using OHAttributedLabel, I also tried using the methods in it's NSAttributedString+Attributes.h category, but those return blue underlined links as well:
-(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{
NSMutableAttributedString* mutableAttributedText = [self.label.attributedText mutableCopy];
[mutableAttributedText setLink:[NSURL URLWithString:#"http://www.somewhere.net"] range:range];
[mutableAttributedText setTextColor:color range:range];
[mutableAttributedText setTextUnderlineStyle:kCTUnderlineStyleNone range:range];
self.label.attributedText = mutableAttributedText;
}
If I comment out the line setting the links in each version, the text gets colored to what I pass in - that works. It just seems like setting the link is overriding this and turning it back to blue.
Unfortunately the apple docs page I found shows how to set the link text to blue and underline it, exactly what I don't need:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html
So I ended up using TTTAttributedLabel:
-(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{
NSMutableAttributedString* newTextWithLinks = [self.label.attributedText mutableCopy];
NSURL *url = [NSURL URLWithString:#"http://www.reddit.com"];
self.label.linkAttributes = #{NSForegroundColorAttributeName: color,
NSUnderlineStyleAttributeName: #(NSUnderlineStyleNone)};
[self.label addLinkToURL:url withRange:range];
}
I found that OHAttributedLabel actually does have methods to set links and declare colors and underline styles for those links. However, I wanted the links to be different colors based on a parameter. TTTAttributedLabel allows this by letting you set it's linkAttributes property for each link you create.
I am using TTTAttributedLabel. I wanted to change the color of the linked text, and keep it underlined. Pim's answer looked great, but didn't work for me. Here's what did work:
label.linkAttributes = #{ (id)kCTForegroundColorAttributeName: [UIColor magentaColor],
(id)kCTUnderlineStyleAttributeName : [NSNumber numberWithInt:NSUnderlineStyleSingle] };
Note: if you don't want the text underlined, then remove the kCTUnderlineStyleAttributeName key from the dictionary.
Here's is my improved version of Ramsel's already great answer.
I believe it's much more readable, and I hope it will come to good use.
label.linkAttributes = #{ NSForegroundColorAttributeName: [UIColor whiteColor],
NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleSingle] };
Here's a list of other attibute names.
If you're using a UITextView, you might have to change the tintColor property to change the link color.
If you're like me and really don't want to use TTT (or need it in your own custom implementation where you're drawing in other weird ways):
First, subclass NSLayoutManager and then override as follows:
- (void)showCGGlyphs:(const CGGlyph *)glyphs
positions:(const CGPoint *)positions
count:(NSUInteger)glyphCount
font:(UIFont *)font
matrix:(CGAffineTransform)textMatrix
attributes:(NSDictionary *)attributes
inContext:(CGContextRef)graphicsContext
{
UIColor *foregroundColor = attributes[NSForegroundColorAttributeName];
if (foregroundColor)
{
CGContextSetFillColorWithColor(graphicsContext, foregroundColor.CGColor);
}
[super showCGGlyphs:glyphs
positions:positions
count:glyphCount
font:font
matrix:textMatrix
attributes:attributes
inContext:graphicsContext];
}
This is more or less telling the layout manager to actually respect NSForegroundColorAttributeName from your attributed string always instead of the weirdness Apple has internally for links.
If all you need to do is get a layout manager which draws correctly (as I needed), you can stop here. If you need an actually UILabel, it's painful but possible.
First, again, subclass UILabel and slap in all of these methods.
- (NSTextStorage *)textStorage
{
if (!_textStorage)
{
_textStorage = [[NSTextStorage alloc] init];
[_textStorage addLayoutManager:self.layoutManager];
[self.layoutManager setTextStorage:_textStorage];
}
return _textStorage;
}
- (NSTextContainer *)textContainer
{
if (!_textContainer)
{
_textContainer = [[NSTextContainer alloc] init];
_textContainer.lineFragmentPadding = 0;
_textContainer.maximumNumberOfLines = self.numberOfLines;
_textContainer.lineBreakMode = self.lineBreakMode;
_textContainer.widthTracksTextView = YES;
_textContainer.size = self.frame.size;
[_textContainer setLayoutManager:self.layoutManager];
}
return _textContainer;
}
- (NSLayoutManager *)layoutManager
{
if (!_layoutManager)
{
// Create a layout manager for rendering
_layoutManager = [[PRYLayoutManager alloc] init];
_layoutManager.delegate = self;
[_layoutManager addTextContainer:self.textContainer];
}
return _layoutManager;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Update our container size when the view frame changes
self.textContainer.size = self.bounds.size;
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
CGSize size = frame.size;
size.width = MIN(size.width, self.preferredMaxLayoutWidth);
size.height = 0;
self.textContainer.size = size;
}
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
CGSize size = bounds.size;
size.width = MIN(size.width, self.preferredMaxLayoutWidth);
size.height = 0;
self.textContainer.size = size;
}
- (void)setPreferredMaxLayoutWidth:(CGFloat)preferredMaxLayoutWidth
{
[super setPreferredMaxLayoutWidth:preferredMaxLayoutWidth];
CGSize size = self.bounds.size;
size.width = MIN(size.width, self.preferredMaxLayoutWidth);
self.textContainer.size = size;
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
// Use our text container to calculate the bounds required. First save our
// current text container setup
CGSize savedTextContainerSize = self.textContainer.size;
NSInteger savedTextContainerNumberOfLines = self.textContainer.maximumNumberOfLines;
// Apply the new potential bounds and number of lines
self.textContainer.size = bounds.size;
self.textContainer.maximumNumberOfLines = numberOfLines;
// Measure the text with the new state
CGRect textBounds;
#try
{
NSRange glyphRange = [self.layoutManager
glyphRangeForTextContainer:self.textContainer];
textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer];
// Position the bounds and round up the size for good measure
textBounds.origin = bounds.origin;
textBounds.size.width = ceilf(textBounds.size.width);
textBounds.size.height = ceilf(textBounds.size.height);
}
#finally
{
// Restore the old container state before we exit under any circumstances
self.textContainer.size = savedTextContainerSize;
self.textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines;
}
return textBounds;
}
- (void)setAttributedText:(NSAttributedString *)attributedText
{
// Pass the text to the super class first
[super setAttributedText:attributedText];
[self.textStorage setAttributedString:attributedText];
}
- (CGPoint)_textOffsetForGlyphRange:(NSRange)glyphRange
{
CGPoint textOffset = CGPointZero;
CGRect textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer];
CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0f;
if (paddingHeight > 0)
{
textOffset.y = paddingHeight;
}
return textOffset;
}
- (void)drawTextInRect:(CGRect)rect
{
// Calculate the offset of the text in the view
CGPoint textOffset;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
textOffset = [self _textOffsetForGlyphRange:glyphRange];
// Drawing code
[self.layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset];
// for debugging the following 2 line should produce the same results
[self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset];
//[super drawTextInRect:rect];
}
Shamelessly taken from here. Incredibly work on the original author's part for working this all out.
Swift 2.3 example for TTTAttributedLabel:
yourLabel.linkAttributes = [
NSForegroundColorAttributeName: UIColor.grayColor(),
NSUnderlineStyleAttributeName: NSNumber(bool: true)
]
yourLabel.activeLinkAttributes = [
NSForegroundColorAttributeName: UIColor.grayColor().colorWithAlphaComponent(0.8),
NSUnderlineStyleAttributeName: NSNumber(bool: false)
]
Swift 4
yourLabel.linkAttributes = [
.foregroundColor: UIColor.grayColor(),
.underlineStyle: NSNumber(value: true)
]
yourLabel.activeLinkAttributes = [
.foregroundColor: UIColor.grayColor().withAlphaComponent(0.7),
.underlineStyle: NSNumber(value: false)
]
For Swift 3 following way worked out for me using TTTAttributedLabel:
1) Add a label on the storyboard and define its class to be TTTAttributedLabel
2) In code define
#IBOutlet var termsLabel: TTTAttributedLabel!
3) Then in ViewDidLoad write these lines
termsLabel.attributedText = NSAttributedString(string: "By using this app you agree to the Privacy Policy & Terms & Conditions.")
guard let labelString = termsLabel.attributedText else {
return
}
guard let privacyRange = labelString.string.range(of: "Privacy Policy") else {
return
}
guard let termsConditionRange = labelString.string.range(of: "Terms & Conditions") else {
return
}
let privacyNSRange: NSRange = labelString.string.nsRange(from: privacyRange)
let termsNSRange: NSRange = labelString.string.nsRange(from: termsConditionRange)
termsLabel.addLink(to: URL(string: "privacy"), with: privacyNSRange)
termsLabel.addLink(to: URL(string: "terms"), with: termsNSRange)
termsLabel.delegate = self
let attributedText = NSMutableAttributedString(attributedString: termsLabel.attributedText!)
attributedText.addAttributes([NSFontAttributeName : UIFont(name: "Roboto-Medium", size: 12)!], range: termsNSRange)
attributedText.addAttributes([NSFontAttributeName : UIFont(name: "Roboto-Medium", size: 12)!], range: privacyNSRange)
attributedText.addAttributes([kCTForegroundColorAttributeName as String: UIColor.orange], range: termsNSRange)
attributedText.addAttributes([kCTForegroundColorAttributeName as String: UIColor.green], range: privacyNSRange)
attributedText.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleNone.rawValue], range: termsNSRange)
attributedText.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleNone.rawValue], range: privacyNSRange)
termsLabel.attributedText = attributedText
It would look like this
4) Finally write the delegate function of TTTAttributedLabel so that you can open links on tap
public func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
switch url.absoluteString {
case "privacy":
SafariBrowser.open("http://google.com", presentingViewController: self)
case "terms":
SafariBrowser.open("http://google.com", presentingViewController: self)
default:
break
}
}
Update for Swift 4.2
For Swift 4.2, there are some changes in step 3, all other steps would remain same as above:
3) In ViewDidLoad write these lines
termsLabel.attributedText = NSAttributedString(string: "By using this app you agree to the Privacy Policy & Terms & Conditions.")
guard let labelString = termsLabel.attributedText else {
return
}
guard let privacyRange = labelString.string.range(of: "Privacy Policy") else {
return
}
guard let termsConditionRange = labelString.string.range(of: "Terms & Conditions") else {
return
}
let privacyNSRange: NSRange = labelString.string.nsRange(from: privacyRange)
let termsNSRange: NSRange = labelString.string.nsRange(from: termsConditionRange)
termsLabel.addLink(to: URL(string: "privacy"), with: privacyNSRange)
termsLabel.addLink(to: URL(string: "terms"), with: termsNSRange)
termsLabel.delegate = self
let attributedText = NSMutableAttributedString(attributedString: termsLabel.attributedText!)
attributedText.addAttributes([NSAttributedString.Key.font : UIFont(name: "Roboto-Regular", size: 12)!], range: termsNSRange)
attributedText.addAttributes([NSAttributedString.Key.font : UIFont(name: "Roboto-Regular", size: 12)!], range: privacyNSRange)
attributedText.addAttributes([kCTForegroundColorAttributeName as NSAttributedString.Key : UIColor.orange], range: termsNSRange)
attributedText.addAttributes([kCTForegroundColorAttributeName as NSAttributedString.Key : UIColor.green], range: privacyNSRange)
attributedText.addAttributes([NSAttributedString.Key.underlineStyle: 0], range: termsNSRange)
attributedText.addAttributes([NSAttributedString.Key.underlineStyle: 0], range: privacyNSRange)
Swift 3 example for TTTAttributedLabel:
yourLabel.linkAttributes = [
NSForegroundColorAttributeName: UIColor.green,
NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleNone.rawValue)
]
yourLabel.activeLinkAttributes = [
NSForegroundColorAttributeName: UIColor.green,
NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleDouble.rawValue)
]
For Swift 3 using TTTAttributedLabel
let title: NSString = "Fork me on GitHub!"
var attirutedDictionary = NSMutableDictionary(dictionary:attributedLabel.linkAttributes)
attirutedDictionary[NSForegroundColorAttributeName] = UIColor.red
attirutedDictionary[NSUnderlineStyleAttributeName] = NSNumber(value: NSUnderlineStyle.styleNone.rawValue)
attributedLabel.attributedText = NSAttributedString(string: title as String)
attributedLabel.linkAttributes = attirutedDictionary as! [AnyHashable: Any]
let range = subtitleTitle.range(of: "me")
let url = URL(string: "http://github.com/mattt/")
attributedLabel.addLink(to: url, with: range)
Swift 4.0 :
Short and simple
let LinkAttributes = NSMutableDictionary(dictionary: testLink.linkAttributes)
LinkAttributes[NSAttributedStringKey.underlineStyle] = NSNumber(value: false)
LinkAttributes[NSAttributedStringKey.foregroundColor] = UIColor.black // Whatever your label color
testLink.linkAttributes = LinkAttributes as NSDictionary as! [AnyHashable: Any]
It's better to use UITextView with "Link" feature enabled. In this case you can do it with one line:
Swift 4:
// apply link attributes to label.attributedString, then
textView.tintColor = UIColor.red // any color you want
Full example:
let attributedString = NSMutableAttributedString(string: "Here is my link")
let range = NSRange(location: 7, length:4)
attributedString.addAttribute(.link, value: "http://google.com", range: range)
attributedString.addAttribute(.underlineStyle, value: 1, range: range)
attributedString.addAttribute(.underlineColor, value: UIColor.red, range: range)
textView.tintColor = UIColor.red // any color you want
Or you can apply attributes to links only:
textView.linkTextAttributes = [
.foregroundColor: UIColor.red
.underlineStyle: 1,
.underlineColor: UIColor.red
]
Related
How to create CGPathRef from UILabel text?
I'm going to make an shadow path for UILabel, which outlines the text of it. Is the UILabel text somehow convertable to CGPath?
I’d suggest that the easiest approach is set the shadow properties of the layer. In Objective-C: self.label.layer.shadowColor = UIColor.blackColor.CGColor; self.label.layer.shadowRadius = 10; self.label.layer.shadowOpacity = 1; self.label.layer.shadowOffset = CGSizeZero; In Swift: label.layer.shadowColor = UIColor.black.cgColor label.layer.shadowRadius = 10 label.layer.shadowOpacity = 1 label.layer.shadowOffset = .zero Yielding: You said: But, in layers I have some additional content, where I don't want to add the shadow. If you have subviews or sublayers that are interfering with the shadow, I’d suggest you move that content out of the label and into their own view hierarchy. It’s hard to be specific without knowing what sublayers/subviews you’ve added to your label. You said: ... I need opacity of the shadow too and it's impossible without layers. That’s not entirely true. You can use the NSAttributedString pattern and specify the alpha as part of the shadowColor. E.g. in Objective-C: NSShadow *shadow = [[NSShadow alloc] init]; shadow.shadowOffset = CGSizeZero; shadow.shadowBlurRadius = 20; shadow.shadowColor = [UIColor.blackColor colorWithAlphaComponent:1]; NSDictionary<NSAttributedStringKey, id> *attributes = #{ NSShadowAttributeName: shadow }; NSMutableAttributedString *string = [self.label.attributedText mutableCopy]; [string setAttributes:attributes range:NSMakeRange(0, string.length)]; self.label.attributedText = string; Or in Swift: let shadow = NSShadow() shadow.shadowOffset = .zero shadow.shadowBlurRadius = 20 shadow.shadowColor = UIColor.black.withAlphaComponent(1) let attributes: [NSAttributedString.Key: Any] = [.shadow: shadow] guard let string = label.attributedText?.mutableCopy() as? NSMutableAttributedString else { return } string.setAttributes(attributes, range: NSRange(location: 0, length: string.length)) label.attributedText = string
Give UITextView a clickable URL link
Hi I've been on this problem for a while now, I read a couple of posts already an I can't understand how to make a clickable UITextView that sends on internet. Here is my code: func transformText(text: String, underlined: Bool, linkURL: String) -> NSAttributedString { let textRange = NSMakeRange(0, text.characters.count) let attributedText = NSMutableAttributedString(string: text) if underlined{ attributedText.addAttribute(NSUnderlineStyleAttributeName , value: NSUnderlineStyle.styleSingle.rawValue, range: textRange) attributedText.addAttribute(NSUnderlineColorAttributeName , value: UIColor.lightGray, range: textRange) } attributedText.addAttribute(NSFontAttributeName , value: UIFont(name: "Helvetica-Light", size: 17)!, range: textRange) attributedText.addAttribute(NSForegroundColorAttributeName , value: UIColor.lightGray, range: textRange) if(linkURL != "") { let attrib = [NSLinkAttributeName: NSURL(string: linkURL)!] attributedText.addAttributes(attrib, range: textRange) } return attributedText } Here is how i call it: TextContent.attributedText = transformText(text: self.TelBox.TextContent.text, underlined: true, linkURL: "https://www.google.fr")` Thanks in advance
Select the UITextView on storyboard and go to 'Show the Attributes inspector', then in 'behavior' unselects 'Editable' and in 'Data Detector' select 'Link'. Then go to 'Sohw the Identity inspector' and in 'traits' select 'Link' and 'Selected'.
You must set editable YES or set selectable YES NSString *text = #"this is google web link"; UITextView *textView = [[UITextView alloc] init]; NSDictionary *dictionary = #{NSFontAttributeName:[UIFont systemFontOfSize:10],NSForegroundColorAttributeName:[UIColor whiteColor]}; NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithString:text attributes:dictionary]; textView.attributedText = attributeStr; [attributeStr addAttributes:#{NSLinkAttributeName: [NSURL URLWithString:#"http://www.google.com"]} range:[_readMessage rangeOfString:#"google web link"]]; textView.attributedText = attributeStr; textView.editable = NO; textView.selectable = YES; textView.delegate = self; google link will reponse by delegate - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
The fact is you want to make UITextView to make clickable requires you to make UITextView non editable because when you click on text it will lunch keyboard. To prevent this you can use textView.isEditable = false Hope it helps.
Make sure you've also enabled data type detection. For hyperlinks: theTextView.dataDetectorTypes = [.link]
Underline UIlabel so the underlining always reaches the bounds
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]; }
Set UILabel line spacing
How can I modify the gap between lines (line spacing) in a multiline UILabel?
Edit: Evidently NSAttributedString will do it, on iOS 6 and later. Instead of using an NSString to set the label's text, create an NSAttributedString, set attributes on it, then set it as the .attributedText on the label. The code you want will be something like this: NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:#"Sample text"]; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; [style setLineSpacing:24]; [attrString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, strLength)]; uiLabel.attributedText = attrString; NSAttributedString's old attributedStringWithString did the same thing, but now that is being deprecated. For historical reasons, here's my original answer: Short answer: you can't. To change the spacing between lines of text, you will have to subclass UILabel and roll your own drawTextInRect, create multiple labels, or use a different font (perhaps one edited for a specific line height, see Phillipe's answer). Long answer: In the print and online world, the space between lines of text is known as "leading" (rhymes with 'heading', and comes from the lead metal used decades ago). Leading is a read-only property of UIFont, which was deprecated in 4.0 and replaced by lineHeight. As far as I know, there's no way to create a font with a specific set of parameters such as lineHeight; you get the system fonts and any custom font you add, but can't tweak them once installed. There is no spacing parameter in UILabel, either. I'm not particularly happy with UILabel's behavior as is, so I suggest writing your own subclass or using a 3rd-party library. That will make the behavior independent of your font choice and be the most reusable solution. I wish there was more flexibility in UILabel, and I'd be happy to be proven wrong!
Starting in ios 6 you can set an attributed string in the UILabel: NSString *labelText = #"some text"; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText]; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineSpacing:40]; [attributedString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [labelText length])]; cell.label.attributedText = attributedString ;
You can control line spacing in the storyboard: duplicate question
From Interface Builder: Programmatically: SWift 4 Using label extension extension UILabel { func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) { guard let labelText = self.text else { return } let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = lineSpacing paragraphStyle.lineHeightMultiple = lineHeightMultiple let attributedString:NSMutableAttributedString if let labelattributedText = self.attributedText { attributedString = NSMutableAttributedString(attributedString: labelattributedText) } else { attributedString = NSMutableAttributedString(string: labelText) } // Line spacing attribute attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length)) self.attributedText = attributedString } } Now call extension function let label = UILabel() let stringValue = "How to\ncontrol\nthe\nline spacing\nin UILabel" // Pass value for any one argument - lineSpacing or lineHeightMultiple label.setLineSpacing(lineSpacing: 2.0) . // try values 1.0 to 5.0 // or try lineHeightMultiple //label.setLineSpacing(lineHeightMultiple = 2.0) // try values 0.5 to 2.0 Or using label instance (Just copy & execute this code to see result) let label = UILabel() let stringValue = "Set\nUILabel\nline\nspacing" let attrString = NSMutableAttributedString(string: stringValue) var style = NSMutableParagraphStyle() style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48 style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40 // Line spacing attribute attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count)) // Character spacing attribute attrString.addAttribute(NSAttributedStringKey.kern, value: 2, range: NSMakeRange(0, attrString.length)) label.attributedText = attrString Swift 3 let label = UILabel() let stringValue = "Set\nUILabel\nline\nspacing" let attrString = NSMutableAttributedString(string: stringValue) var style = NSMutableParagraphStyle() style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48 style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40 attrString.addAttribute(NSParagraphStyleAttributeName, value: style, range: NSRange(location: 0, length: stringValue.characters.count)) label.attributedText = attrString
My solution was to patch the font file itself and fix its line height definitely. http://mbauman.net/geek/2009/03/15/minor-truetype-font-editing-on-a-mac/ I had to modify 'lineGap', 'ascender', 'descender' in the 'hhea' block (as in the blog example).
This guy created a class to get line-height (without using CoreText, as MTLabel library) : https://github.com/LemonCake/MSLabel
Best thing I found is: https://github.com/mattt/TTTAttributedLabel It's a UILabel subclass so you can just drop it in, and then to change the line height: myLabel.lineHeightMultiple = 0.85; myLabel.leading = 2;
I've found 3rd Party Libraries Like this one: https://github.com/Tuszy/MTLabel To be the easiest solution.
Here's some swift-code for you to set the line spacing programmatically let label = UILabel() let attributedText = NSMutableAttributedString(string: "Your string") let paragraphStyle = NSMutableParagraphStyle() //SET THIS: paragraphStyle.lineSpacing = 4 //OR SET THIS: paragraphStyle.lineHeightMultiple = 4 //Or set both :) let range = NSMakeRange(0, attributedText.length) attributedText.addAttributes([NSParagraphStyleAttributeName : paragraphStyle], range: range) label.attributedText = attributedText
Of course, Mike's answer doesn't work if you pass the string programmatically. In this case you need to pass a attributed string and change it's style. NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:#"Your \nregular \nstring"]; NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; [style setLineSpacing:4]; [attrString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, attrString.length)]; _label.attributedText = attrString;
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..)