how do I detect and make clickable links in a UILabel NOT using UITextView

送分小仙女□ 提交于 2019-12-01 00:24:04

问题


I am building a chat app and for performance reasons I need to use UILabel's instead of UITextView's to display the chat messages. I have previously used TextView's but with data detection on the scrolling is very slow and choppy.

The problem is there is currently no link/phone/address etc... detection for UILabels.

How can I know where a link or phone number exists in a string, and then highlight and make it clickable within a UILabel?

I have read many articles on how to add attributes for links to do just that but they have all been links which you know the range or substring of.

I would like to take any string and find out whether is contains links and where those links are and then add the tapGestureRecognizer to the label and perform actions based on where the tap occurred.

I have tried to incorporate an external library (TTTAttributedLabel) but I'm using swift and found the documentation for swift to be limited. I did manage to import the library but the links are not being automatically detected.


回答1:


Reference source -

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

It is Converted into swift 4.0

Try this -

Create a sub class for UILabel like below -

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)
 }

}

Within your viewDidLoad method of View controller create an instance of that class like below -

    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
    }
}


来源:https://stackoverflow.com/questions/42240683/how-do-i-detect-and-make-clickable-links-in-a-uilabel-not-using-uitextview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!