NSAttributedString click event in UILabel using Swift

30,557

Solution 1

There's no need to use a separate gesture recognizer as some of the answers state. Instead, you can use attributed text in combination with the UITextViewDelegate's textView:shouldInteractWithURL:inRange:interaction: method to achieve this, ex:

class ViewController: UIViewController, UITextViewDelegate {
    
    @IBOutlet weak var textView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let text = NSMutableAttributedString(string: "Already have an account? ")
        text.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 12), range: NSMakeRange(0, text.length))
        
        let selectablePart = NSMutableAttributedString(string: "Sign in!")
        selectablePart.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 12), range: NSMakeRange(0, selectablePart.length))
        // Add an underline to indicate this portion of text is selectable (optional)
        selectablePart.addAttribute(NSAttributedString.Key.underlineStyle, value: 1, range: NSMakeRange(0,selectablePart.length))
        selectablePart.addAttribute(NSAttributedString.Key.underlineColor, value: UIColor.black, range: NSMakeRange(0, selectablePart.length))
        // Add an NSLinkAttributeName with a value of an url or anything else
        selectablePart.addAttribute(NSAttributedString.Key.link, value: "signin", range: NSMakeRange(0,selectablePart.length))
        
        // Combine the non-selectable string with the selectable string
        text.append(selectablePart)
        
        // Center the text (optional)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.alignment = NSTextAlignment.center
        text.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, text.length))
        
        // To set the link text color (optional)
        textView.linkTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12)]
        // Set the text view to contain the attributed text
        textView.attributedText = text
        // Disable editing, but enable selectable so that the link can be selected
        textView.isEditable = false
        textView.isSelectable = true
        // Set the delegate in order to use textView(_:shouldInteractWithURL:inRange)
        textView.delegate = self
    }
    
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

        // **Perform sign in action here**
        
        return false
    }
}

Solution 2

Language update based on @Lindsey Scott answer :)

Swift 4

class ViewController: UIViewController, UITextViewDelegate {
   
    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let text = NSMutableAttributedString(string: "Already have an account? ")
        text.addAttribute(NSAttributedStringKey.font,
                          value: UIFont.systemFont(ofSize: 12),
                          range: NSRange(location: 0, length: text.length))
        
        let interactableText = NSMutableAttributedString(string: "Sign in!")
        interactableText.addAttribute(NSAttributedStringKey.font,
                                      value: UIFont.systemFont(ofSize: 12),
                                      range: NSRange(location: 0, length: interactableText.length))
        
        // Adding the link interaction to the interactable text
        interactableText.addAttribute(NSAttributedStringKey.link,
                                      value: "SignInPseudoLink",
                                      range: NSRange(location: 0, length: interactableText.length))
        
        // Adding it all together
        text.append(interactableText)
        
        // Set the text view to contain the attributed text
        textView.attributedText = text
        
        // Disable editing, but enable selectable so that the link can be selected
        textView.isEditable = false
        textView.isSelectable = true
        textView.delegate = self
    }
    
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        
        //Code to the respective action
        
        return false
    }
}

Solution 3

Instead of Label you can use a textview to open a View Controller or make substring clickable.

  1. Create an attribute for the string which you want to make clickable

    let linkAttributes = [
        NSLinkAttributeName: NSURL(string: "https://www.apple.com")!,
        NSForegroundColorAttributeName: UIColor.blue
        ] as [String : Any]
    
  2. Make your string an attributed string

    let attributedString = NSMutableAttributedString(string:"My name is Jarvis")
    attributedString.setAttributes(linkAttributes, range: NSMakeRange(5, 10))
    

    You can give your custom range here.

  3. Add an attributed text to your textview

    YourTextView.attributedText = attributedString
    
  4. Then implement the following delegate method of textview to implement interaction for a URL

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        // Here write your code of navigation
        return false
    }
    
  5. If you want to do it with label, click see How can I make a clickable link in an NSAttributedString?.

Solution 4

You can add a tap gesture recognizer to your label/view, or you can embed a link with a custom URL protocol into your attributed string, use a UITextView, and turn on link detection. You would then need to implement the UITextView delegate method for responding to links.

I have a demo project called DatesInSwift (link) on GitHub that implements clickable links in a UITextView. Take a look at the UITextView delegate method textView(_:shouldInteractWithURL:inRange) in ViewController.swift. That's the method that tells the text view that it should respond to the URL.

Then you have to implement a UIApplicationDelegate method to handle the URL. The sample app uses application(_:openURL:sourceApplication:annotation), which was deprecated in iOS 9. For new development you should use application(_:openURL:options:) instead.

You will also need to add a CFBundleURLTypes / CFBundleURLSchemes entry to your info.plist to register a custom URL scheme (like myompany.myapp.loginURL) in order for clicking on an embedded URL to invoke your app.

Solution 5

Swift 4.2 and Xcode 11

Using a TextView it's much easier:

func setupContactUsInTextView() {
    let text = NSMutableAttributedString(string: "Contact us at email ")
    text.addAttribute(NSAttributedStringKey.font,
                      value: UIFont.systemFont(ofSize: 17),
                      range: NSRange(location: 0, length: text.length))

    let interactableText = NSMutableAttributedString(string: "[email protected]")
    interactableText.addAttribute(NSAttributedStringKey.font,
                                  value: UIFont.systemFont(ofSize: 17),
                                  range: NSRange(location: 0, length: interactableText.length))


    interactableText.addAttribute(NSAttributedStringKey.link,
                                  value: "[email protected]",
                                  range: NSRange(location: 0, length: interactableText.length))


    text.append(interactableText)


    contactUsTextView.attributedText = text
    contactUsTextView.textAlignment = .center
    contactUsTextView.isEditable = false
    contactUsTextView.isSelectable = true
    contactUsTextView.delegate = self
}

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    print("open website here...")
    return false
}
Share:
30,557
Rishu Agrawal
Author by

Rishu Agrawal

Updated on August 03, 2022

Comments

  • Rishu Agrawal
    Rishu Agrawal almost 2 years

    Suppose I have an AttributedString: "Already have an account? Sign in!".

    I am placing this String in UILabel. Now when a user clicks on "Sign in!", the current viewController should go to another viewController or some function should be called while clicking on sign in.

    Any code or suggestion should be fine.

  • vacawama
    vacawama almost 8 years
    and set YOUR_LABEL.userInteractionEnabled = true.
  • Rishu Agrawal
    Rishu Agrawal almost 8 years
    But this is calling method for the whole string, not just "Sign in!"
  • Vishal Sonawane
    Vishal Sonawane almost 8 years
    Then simply use two labels and add tapgesture to signIn label.
  • Rishu Agrawal
    Rishu Agrawal almost 8 years
    @VishalSonawane : you're right, but I want this using single label.
  • Vishal Sonawane
    Vishal Sonawane almost 8 years
    Then have a look at this stackoverflow.com/questions/8811909/… and perform your actions when your word matches to Sign In(you will understand what I mean to say after going through that link).
  • Larme
    Larme almost 8 years
    @RishuAgrawal: You can disable the edition of a UITextView, and customize it too looks like a UILabel.
  • Larme
    Larme almost 8 years
    Why use a tap gesture when you could simply use NSLinkAttributeName?
  • Rishu Agrawal
    Rishu Agrawal almost 8 years
    Can you explain how to use that?
  • Duncan C
    Duncan C almost 8 years
    See the edit to my answer. There are a fair number of steps.
  • Rishu Agrawal
    Rishu Agrawal almost 8 years
    textView.delegate = self is giving error Cannot assign value of type 'ViewController' to type 'UITextViewDelegate?'
  • Lyndsey Scott
    Lyndsey Scott almost 8 years
    @RishuAgrawal Did you add UITextViewDelegate to the view controller as in the first line of my code: class ViewController: UIViewController, UITextViewDelegate { ?
  • PRADIP KUMAR
    PRADIP KUMAR over 5 years
    How could I identify If there are two links and based on click If i want to apply different actions..
  • Peter Mortensen
    Peter Mortensen about 3 years
    An explanation would be in order. E.g., what is the (significant) difference(s)?
  • Abishek Thangaraj
    Abishek Thangaraj about 2 years
    The Answer is for whole string. we need the touch event for particular string.