如何在UILabel中使用UITextView检测并制作可点击链接

时间:2017-02-15 04:19:17

标签: swift uilabel uitextview tttattributedlabel datadetectortypes

我正在构建一个聊天应用程序,出于性能原因,我需要使用UILabel而不是UITextView来显示聊天消息。我之前使用的是TextView,但滚动时的数据检测速度非常慢且不连贯。

问题是目前没有链接/电话/地址等... UILabels的检测。

如何知道字符串中存在链接或电话号码的位置,然后在UILabel中突出显示并使其可点击?

我已经阅读了很多关于如何添加链接属性的文章,但是它们都是你知道范围或子串的链接。

我想获取任何字符串并找出是否包含链接以及这些链接的位置,然后将tapGestureRecognizer添加到标签并根据点击发生的位置执行操作。

我试图合并一个外部库(TTTAttributedLabel)但是我使用swift并发现swift的文档有限。我确实设法导入库但是没有自动检测到链接。

1 个答案:

答案 0 :(得分:5)

参考来源 -

Create tap-able "links" in the NSAttributedString of a UILabel?

它被转换为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()
    // Do any additional setup after loading the view, typically from a nib.

    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); // for the word "link" in the string above

    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

        // DO YOUR STUFF HERE
    }
}