Tap on a part of text of UILabel Tap on a part of text of UILabel ios ios

Tap on a part of text of UILabel


#swift 4.2Please find the solution here for getting specific text action of Label.

enter image description here

  1. Label declaration

    @IBOutlet weak var lblTerms: UILabel!
  2. Set attributed text to the label

    let text = "Please agree for Terms & Conditions."lblTerms.text = textself.lblTerms.textColor =  UIColor.whitelet underlineAttriString = NSMutableAttributedString(string: text)let range1 = (text as NSString).range(of: "Terms & Conditions.")     underlineAttriString.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: range1)     underlineAttriString.addAttribute(NSAttributedString.Key.font, value: UIFont.init(name: Theme.Font.Regular, size: Theme.Font.size.lblSize)!, range: range1)     underlineAttriString.addAttribute(NSAttributedString.Key.foregroundColor, value: Theme.color.primaryGreen, range: range1)lblTerms.attributedText = underlineAttriStringlblTerms.isUserInteractionEnabled = truelblTerms.addGestureRecognizer(UITapGestureRecognizer(target:self, action: #selector(tapLabel(gesture:))))

It looks like the above image.

  1. Add the tapLabel action method to the controller

    @IBAction func tapLabel(gesture: UITapGestureRecognizer) {let termsRange = (text as NSString).range(of: "Terms & Conditions")// comment for now//let privacyRange = (text as NSString).range(of: "Privacy Policy")if gesture.didTapAttributedTextInLabel(label: lblTerms, inRange: termsRange) {    print("Tapped terms")} else if gesture.didTapAttributedTextInLabel(label: lblTerms, inRange: privacyRange) {    print("Tapped privacy") } else {                    print("Tapped none")}}
  2. Add UITapGestureRecognizer extension

    extension UITapGestureRecognizer {    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {        // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage        let layoutManager = NSLayoutManager()        let textContainer = NSTextContainer(size: CGSize.zero)        let textStorage = NSTextStorage(attributedString: label.attributedText!)        // Configure layoutManager and textStorage        layoutManager.addTextContainer(textContainer)        textStorage.addLayoutManager(layoutManager)        // Configure textContainer        textContainer.lineFragmentPadding = 0.0        textContainer.lineBreakMode = label.lineBreakMode        textContainer.maximumNumberOfLines = label.numberOfLines        let labelSize = label.bounds.size        textContainer.size = labelSize        // Find the tapped character location and compare it to the specified range        let locationOfTouchInLabel = self.location(in: label)        let textBoundingBox = layoutManager.usedRect(for: textContainer)        //let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,        //(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)        //let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,        // locationOfTouchInLabel.y - textContainerOffset.y);        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)        return NSLocationInRange(indexOfCharacter, targetRange)    }}

Make sure to do:

lblTerms.isUserInteractionEnabled = true


After having several issues with this kind of stuff, using a lot of different librairies, etc... I found an interesting solution:http://samwize.com/2016/03/04/how-to-create-multiple-tappable-links-in-a-uilabel/

It's about to extend UITapGestureRegonizer and detect if the tap is in the range of the string when triggered.

Here is the updated Swift 4 version of this extension:

extension UITapGestureRecognizer {    func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {        // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage        let layoutManager = NSLayoutManager()        let textContainer = NSTextContainer(size: CGSize.zero)        let textStorage = NSTextStorage(attributedString: label.attributedText!)        // Configure layoutManager and textStorage        layoutManager.addTextContainer(textContainer)        textStorage.addLayoutManager(layoutManager)        // Configure textContainer        textContainer.lineFragmentPadding = 0.0        textContainer.lineBreakMode = label.lineBreakMode        textContainer.maximumNumberOfLines = label.numberOfLines        let labelSize = label.bounds.size        textContainer.size = labelSize        // Find the tapped character location and compare it to the specified range        let locationOfTouchInLabel = self.location(in: label)        let textBoundingBox = layoutManager.usedRect(for: textContainer)        let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y)        let locationOfTouchInTextContainer = CGPoint(x: locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y)        let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)        return NSLocationInRange(indexOfCharacter, targetRange)    }}

To simplify range conversion, you also need this Range extension

extension Range where Bound == String.Index {    var nsRange:NSRange {        return NSRange(location: self.lowerBound.encodedOffset,                   length: self.upperBound.encodedOffset -                    self.lowerBound.encodedOffset)    }}

Once you have this extension, you can add a tap gesture to your label:

let tap = UITapGestureRecognizer(target: self, action: #selector(tapLabel(tap:)))self.yourLabel.addGestureRecognizer(tap)self.yourLabel.isUserInteractionEnabled = true

Here is the function to handle the tap:

@objc func tapLabel(tap: UITapGestureRecognizer) {    guard let range = self.yourLabel.text?.range(of: "Substring to detect")?.nsRange else {        return    }    if tap.didTapAttributedTextInLabel(label: self.yourLabel, inRange: range) {        // Substring tapped    }}


This is a real easy alternative for anyone who is willing to use a textView. I realize this question is about a UILabel but if you read the comments on some of the answers they don't work for some people and some of them are very code heavy which isn't very good for beginners. You can do this in 11 simple steps if your willing to swap out a UILabel for a UITextView.

You can use NSMutableAttributedString and a UITextView. The UITextView has a delegate method: func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { ... }. Once you set the part of the string that you want to make tappable the delegate method will activate it.

The 11 steps are listed below in the comments above each piece of code.

// 1st **BE SURE TO INCLUDE** UITextViewDelegate to the view controller's classclass VewController: UIViewController, UITextViewDelegate {    // 2nd use a programmatic textView or use the textView from your storyboard    lazy var yourTextView: UITextView = {        let textView = UITextView()        textView.textAlignment = .center        textView.isEditable = false        textView.showsVerticalScrollIndicator = false        return textView    }()   override func viewDidLoad() {        super.viewDidLoad()        // 3rd in viewDidLoad set the textView's delegate        yourTextView.delegate = self        // 4th create the first piece of the string you don't want to be tappable        let regularText = NSMutableAttributedString(string: "any text ", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17), NSAttributedStringKey.foregroundColor: UIColor.black])        // 5th create the second part of the string that you do want to be tappable. I used a blue color just so it can stand out.        let tappableText = NSMutableAttributedString(string: "READ MORE")        tappableText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 17), range: NSMakeRange(0, tappableText.length))        tappableText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: NSMakeRange(0, tappableText.length))        // 6th this ISN'T NECESSARY but this is how you add an underline to the tappable part. I also used a blue color so it can match the tappableText and set the value to 1 for the line height. The length of the underline is based on the tappableText's length using NSMakeRange(0, tappableText.length)        tappableText.addAttribute(NSAttributedString.Key.underlineStyle, value: 1, range: NSMakeRange(0, tappableText.length))        tappableText.addAttribute(NSAttributedString.Key.underlineColor, value: UIColor.blue, range: NSMakeRange(0, tappableText.length))        // 7th this is the important part that connects the tappable link to the delegate method in step 11        // use NSAttributedString.Key.link and the value "makeMeTappable" to link the NSAttributedString.Key.link to the method. FYI "makeMeTappable" is a name I choose for clarity, you can use anything like "anythingYouCanThinkOf"        tappableText.addAttribute(NSAttributedString.Key.link, value: "makeMeTappable", range: NSMakeRange(0, tappableText.length))        // 8th *** important append the tappableText to the regularText ***        regularText.append(tappableText)        // 9th set the regularText to the textView's attributedText property        yourTextView.attributedText = regularText    }   // 10th add the textView's delegate method that activates urls. Make sure to return false for the tappable part   func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {            // 11th use the value from the 7th step to trigger the url inside this method        if URL.absoluteString == "makeMeTappable" {            // in this situation I'm using the tappableText to present a view controller but it can be used for whatever you trying to do            let someVC = SomeController()            let navVC = UINavigationController(rootViewController: someVC)            present(navVC, animated: true, completion: nil)            return false // *** IMPORTANT return false for this to actually work ***        }        return true    }}