I want to change the color of a link within a UILabel. I've found loads of past questions on how to do this for a UITextView, and past questions with answers in Obj-C (but can't translate these to Swift as properties that did exist in Obj-c no longer do such as NSMutableAttributedString.linkTextAttribtues for example). But I cannot find how to do this for a UILabel and in Swift 4.
For default NSAttributedString.Key.link color will be blue.
If you need custom colors for links you can set the attribute as NSAttributedString.Key.attachment instead of .link and set the foreground and underline colors like this:
let linkCustomAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 14),
NSAttributedString.Key.foregroundColor: UIColor.red,
NSAttributedString.Key.underlineColor: UIColor.magenta,
NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
NSAttributedString.Key.attachment: URL(string: "https://www.google.com")] as [NSAttributedString.Key : Any]
If you need to handle touches on links you can use this custom label class:
import UIKit
public protocol UILabelTapableLinksDelegate: NSObjectProtocol {
func tapableLabel(_ label: UILabelTapableLinks, didTapUrl url: String, atRange range: NSRange)
}
public class UILabelTapableLinks: UILabel {
private var links: [String: NSRange] = [:]
private(set) var layoutManager = NSLayoutManager()
private(set) var textContainer = NSTextContainer(size: CGSize.zero)
private(set) var textStorage = NSTextStorage() {
didSet {
textStorage.addLayoutManager(layoutManager)
}
}
public weak var delegate: UILabelTapableLinksDelegate?
public override var attributedText: NSAttributedString? {
didSet {
if let attributedText = attributedText {
textStorage = NSTextStorage(attributedString: attributedText)
findLinksAndRange(attributeString: attributedText)
} else {
textStorage = NSTextStorage()
links = [:]
}
}
}
public override var lineBreakMode: NSLineBreakMode {
didSet {
textContainer.lineBreakMode = lineBreakMode
}
}
public override var numberOfLines: Int {
didSet {
textContainer.maximumNumberOfLines = numberOfLines
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
isUserInteractionEnabled = true
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
}
public override func layoutSubviews() {
super.layoutSubviews()
textContainer.size = bounds.size
}
private func findLinksAndRange(attributeString: NSAttributedString) {
links = [:]
let enumerationBlock: (Any?, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void = { [weak self] value, range, isStop in
guard let strongSelf = self else { return }
if let value = value {
let stringValue = "\(value)"
strongSelf.links[stringValue] = range
}
}
attributeString.enumerateAttribute(.link, in: NSRange(0..<attributeString.length), options: [.longestEffectiveRangeNotRequired], using: enumerationBlock)
attributeString.enumerateAttribute(.attachment, in: NSRange(0..<attributeString.length), options: [.longestEffectiveRangeNotRequired], using: enumerationBlock)
}
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let locationOfTouch = touches.first?.location(in: self) else {
return
}
textContainer.size = bounds.size
let indexOfCharacter = layoutManager.glyphIndex(for: locationOfTouch, in: textContainer)
for (urlString, range) in links where NSLocationInRange(indexOfCharacter, range) {
delegate?.tapableLabel(self, didTapUrl: urlString, atRange: range)
return
}
}
}
Setup label in your code:
customLabel.attributedText = <<Your attributed text with custom links>>
customLabel.delegate = self
Implement delegate:
extension YourClass: UILabelTapableLinksDelegate {
func tapableLabel(_ label: UILabelTapableLinks, didTapUrl url: String, atRange range: NSRange) {
print("didTapUrl: ", url)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With