Reputation: 7004
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
Upvotes: 44
Views: 36271
Reputation: 8387
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
]
Upvotes: -2
Reputation: 159
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
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)
Upvotes: 1
Reputation: 2768
If you're using a UITextView
, you might have to change the tintColor
property to change the link color.
Upvotes: 13
Reputation: 2817
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]
Upvotes: -1
Reputation: 434
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)
]
Upvotes: 4
Reputation: 2383
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.
Upvotes: 5
Reputation: 16032
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)
Upvotes: 0
Reputation: 64
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)
]
Upvotes: 0
Reputation: 7004
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.
Upvotes: 62
Reputation: 2148
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.
Upvotes: 23
Reputation: 2128
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.
Upvotes: 22