参考源 -
如何在UILabel的NSAttributedString中创建可点击的“链接”?
已转换为Swift 4.0版本
请使用以下代码创建一个UILabel的子类 -
Swift 4.0
class CustomLabel: UILabel {
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
var textStorage = NSTextStorage() {
didSet {
textStorage.addLayoutManager(layoutManager)
}
}
var onCharacterTapped: ((_ label: UILabel, _ characterIndex: Int) -> Void)?
let tapGesture = UITapGestureRecognizer()
override var attributedText: NSAttributedString? {
didSet {
if let attributedText = attributedText {
textStorage = NSTextStorage(attributedString: attributedText)
} else {
textStorage = NSTextStorage()
}
}
}
override var lineBreakMode: NSLineBreakMode {
didSet {
textContainer.lineBreakMode = lineBreakMode
}
}
override var numberOfLines: Int {
didSet {
textContainer.maximumNumberOfLines = numberOfLines
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
func setUp() {
isUserInteractionEnabled = true
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
tapGesture.addTarget(self, action: #selector(CustomLabel.labelTapped(_:)))
addGestureRecognizer(tapGesture)
}
override func layoutSubviews() {
super.layoutSubviews()
textContainer.size = bounds.size
}
@objc func labelTapped(_ gesture: UITapGestureRecognizer) {
guard gesture.state == .ended else {
return
}
let locationOfTouch = gesture.location(in: gesture.view)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX,
y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x, y: locationOfTouch.y - textContainerOffset.y)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer,
in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
onCharacterTapped?(self, indexOfCharacter)
}
}
在View控制器的viewDidLoad
方法中创建该类的实例,如下所示:
override func viewDidLoad() {
super.viewDidLoad()
let label = CustomLabel()
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil)
let linkRange = NSMakeRange(14, 4);
let linkAttributes: [NSAttributedStringKey : AnyObject] = [
NSAttributedStringKey.foregroundColor : UIColor.blue, NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue as AnyObject,
NSAttributedStringKey.link: "http://www.apple.com" as AnyObject ]
attributedString.setAttributes(linkAttributes, range:linkRange)
label.attributedText = attributedString
label.onCharacterTapped = { label, characterIndex in
}
}
label.onCharacterTapped = { label, characterIndex in print(characterIndex) }
- lucius degeerlet indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
- lucius degeer