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

前端 未结 1 1999
春和景丽
春和景丽 2020-12-19 00:36

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

相关标签:
1条回答
  • 2020-12-19 00:49

    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
        }
    }
    
    0 讨论(0)
提交回复
热议问题