4

I have an attributed string set to UILabel with multiple underlines , colors like below image

enter image description here

and I know How to setup a tap gesture for whole label (with enabling user interaction) and below is my code for what I have done including setting up underline and setting up font colors for multiple ranges.

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var mylabel: UILabel!

    var theString = "I have agree with the terms and conditions and privacy policy"

    override func viewDidLoad() {
        super.viewDidLoad()

        mylabel.text = theString


        let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.printme))
        mylabel.addGestureRecognizer(tap)

        setUnderline(theText: theString)

        // Do any additional setup after loading the view, typically from a nib.
    }

    func printme() {
        print("print this")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func setUnderline(theText : String) {
        //set up underline
        let textRange1 = NSMakeRange(22, 19)
        let textRange2 = NSMakeRange(47, (theText.characters.count-47))
        let attributedText = NSMutableAttributedString(string : theText)
        attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: textRange1)
        attributedText.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: textRange2)


        //setup colors
        attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSRange(location: 22,length: 20))
        attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSRange(location: 47,length: (theText.characters.count-47)))

        mylabel.attributedText = attributedText

    }
  • The tap gesture work for whole label. what I want is when user tap on "terms and conditions" fire a different function and and when user tap on "privacy policy" fire another different function. how can I do that.

Note : I want to fire two different functions one for "terms and conditions" tap, and other for "privacy policy" tap, and do not want to just open links

caldera.sac
  • 4,918
  • 7
  • 37
  • 69
  • Possible duplicate of [iOS UITextView or UILabel with clickable links to actions](http://stackoverflow.com/questions/20541676/ios-uitextview-or-uilabel-with-clickable-links-to-actions) – alexburtnik Nov 09 '16 at 17:21

2 Answers2

3
    import UIKit

    protocol SSGastureDelegate:NSObjectProtocol {
        func callBack()
    }
    class SSUnderLineLabel: UILabel {
        var tapGesture: UITapGestureRecognizer?

      //Make weak refernece for SSGastureDelegate
       weak var delegate:SSGastureDelegate?

        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)!
            self.initialization()
        }
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.initialization()
        }

        func initialization(){
            let newsString: NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
            let textRange = NSString(string: self.text!)
            let substringRange = textRange.range(of: "Terms and Conditions") // You can add here for own specific under line substring
            newsString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: substringRange)
           // self.attributedText = newsString.copy() as? NSAttributedString
            self.attributedText = newsString
            self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapResponse))
            self.isUserInteractionEnabled =  true
            self.addGestureRecognizer(tapGesture!)

        }

        func tapResponse(recognizer: UITapGestureRecognizer) {

            let text = (self.text)!
            let termsRange = (text as NSString).range(of: "Terms and Conditions")
            if (tapGesture?.didTapAttributedTextInLabel(label: self, inRange: termsRange))! {
                print("Tapped terms conditions")
                self.delegate?.callBack()
            }
            else {
                print("Tapped none ")
            }
        }

    }

    //MARK:UITapGestureRecognizer Extension
    //MARK:
    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)
        }

    }*`enter code here`




**How to use it:**
Assign this class for your label and follow these steps:
Demo for use class:

ViewController.swift
//===============

class ViewController: UIViewController,SSGastureDelegate {
 @IBOutlet var underlineLbl: SSUnderLineLabel!

 override func viewDidLoad() {
      super.viewDidLoad()
     self.underlineLbl.delegate = self
 }
//Implement Delegate Method
 func callBack()
    {
//Open a specific vc for underline tap are`enter code here`a.
        let termsconditionsVC = storyboard?.instantiateViewController(withIdentifier: "TermsConditionsVC") as! TermsConditionsVC
        self.present(termsconditions, animated: true, completion: nil)

    }
}*
Saurabh Sharma
  • 187
  • 2
  • 8
0

I see that you need your terms and privacy links to be clickable, the simple way the ios gives is AttributedString, use following code for that:

let theString = "I have agree with the terms and conditions and privacy policy"
let someAttributedString = NSMutableAttributedString(string: theString)
someAttributedString.addAttribute(NSLinkAttributeName, value: "http://www.google.com", range: NSMakeRange(22, 20))
titleLabel.attributedText = someAttributedString

TTTAttributedLabel could be your best solution: https://github.com/TTTAttributedLabel/TTTAttributedLabel

TTTAttributedLabel has following delegate

- (void)attributedLabel:(__unused TTTAttributedLabel *)label
   didSelectLinkWithURL:(NSURL *)url
{
 //do whatever you want
}

Also if you need solution from tap gesture you can find the point on gesture tap and do you calculation to check where it is tapped to perform your action by gestureRecognizer.locationInView method

Devanshu Saini
  • 765
  • 5
  • 24
  • actually this is not exactly what I want, what I want is I want to fire two different functions when click on each attributed text. what I want is set up tap gesture for each attributed strings. not for whole label. and also, this is not working. thanx anyway for your help – caldera.sac Nov 10 '16 at 04:18
  • Please check TTTAttributedLabel as mentioned above, this may help you – Devanshu Saini Nov 10 '16 at 06:29