I have NSAttributed string with links in it and I want to load it inside UILabel. I works fine, however all links are blue Color.
let string = NSMutableAttributedString(attributedString: attributedText)
string.addAttributes([NSForegroundColorAttributeName:linkColor], range: linkRange)
self.attributedText = string
No change to foreground color, setting all other attributes work, like strikethrough style. Just link always stays blue.
NSAttributed string is generated from HTML if that makes any difference.
Ended up doing
class AttributedTextLabel:UILabel {
var attributedString:NSAttributedString?{
didSet{
guard let attributedString = attributedString else {
return
}
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
mutableAttributedString.enumerateAttribute(NSLinkAttributeName, inRange: NSRange(location: 0, length: attributedString.length), options: NSAttributedStringEnumerationOptions.Reverse) {[weak self] (attribute, range, other) in
if let url = attribute as? NSURL {
mutableAttributedString.removeAttribute(NSLinkAttributeName, range: range)
self?.links.append(Link(url: url, range: range))
}
}
self.attributedText = mutableAttributedString
}
}
struct Link {
var url:NSURL
var range:NSRange
}
var links:[Link] = []
var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero
private var textContentSize:CGSize {
let textContainerWidth = frame.width - edgeInsets.left - edgeInsets.right
let textContainerHeight = frame.height - edgeInsets.top - edgeInsets.bottom
return CGSizeMake(textContainerWidth, textContainerHeight)
}
func characterIndexAtPoint(point:CGPoint) -> Int? {
guard let attributedText = attributedText else {
return nil
}
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: textContentSize)
textContainer.lineFragmentPadding = 0.0
textContainer.lineBreakMode = self.lineBreakMode
textContainer.maximumNumberOfLines = self.numberOfLines
layoutManager.addTextContainer(textContainer)
let storage = NSTextStorage(attributedString: attributedText)
storage.addLayoutManager(layoutManager)
let adjustedPoint = CGPointMake(point.x-edgeInsets.left, point.y-edgeInsets.top)
let characterIndex = layoutManager.characterIndexForPoint(point, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
return characterIndex
}
override func drawTextInRect(rect: CGRect) {
return super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
}
private var selectedRange:NSRange?
private var highligtedLink:Link? {
didSet{
let string = self.attributedText as! NSMutableAttributedString
if let oldValue = oldValue {
if let selectedLinkColor = NativeTextKit.TextAttributes.selectedLinkColor.value {
string.addAttributes([
NSForegroundColorAttributeName:selectedLinkColor
], range: oldValue.range)
}
}
if let highligtedLink = highligtedLink {
if let selectedLinkColor = NativeTextKit.TextAttributes.selectedLinkColor.value {
string.addAttributes([
NSForegroundColorAttributeName:selectedLinkColor
], range: highligtedLink.range)
}
}
self.attributedText = string
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else {
return
}
let char = characterIndexAtPoint(touch.locationInView(self))
let string = self.attributedText as! NSMutableAttributedString
highligtedLink = linkForTouch(touch)
string.addAttributes([
NSForegroundColorAttributeName:UIColor.brownColor()
], range: NSMakeRange(char!, 1))
attributedText = string
}
func linkForTouch(touch:UITouch)->Link? {
guard let attributedText = attributedText else {
return nil
}
guard let characterIndex = characterIndexAtPoint(touch.locationInView(self)) else {
return nil
}
return links.filter({NSLocationInRange(characterIndex, $0.range)}).first
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
highligtedLink = nil
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else {
return
}
if let highligtedLink = highligtedLink, let lastTouchedLink = linkForTouch(touch) where highligtedLink.url == lastTouchedLink.url {
urlInteractionHandler?(textView: UITextView(), url:lastTouchedLink.url)
}
}
/// Executed on link interaction
var urlInteractionHandler:URLInteractionHandler?
}
Does the job, took a while to figure out. Because UILabel has its own link formatting ended up
- Remove all links from attributed string once string is set
- Add links and ranges to array
- After link is selected use NSTextContainer to figure out what index the character was
- Find range that character belongs to
- Return link