Search code examples
iosswiftuilabelnsattributedstringuitapgesturerecognizer

How to add function-call (not hyperlink) to part of an NSAttributedString in a UILabel?


I need to add some tapGestures to some part of a text, thats in a UILabel. It seems to be able to make a hyperlink - so I guess its possible to make a functioncall as well, but I'm just not sure how to do it.

Heres my code:

    let regularFont = UIFont(name: Constants.robotoRegular, size: 13)!
    let att1 = [NSFontAttributeName: regularFont]

    let turqoiseFont = UIFont(name: Constants.robotoBold, size: 13)!
    let att2 = [NSFontAttributeName: turqoiseFont]

    let attString1 = NSMutableAttributedString(string: "By creating a profile, I accept ", attributes: att1)
    attString1.addAttribute(NSForegroundColorAttributeName, value: UIColor.darkGrayColor(), range: NSMakeRange(0, attString1.length))

    let attString2 = NSMutableAttributedString(string: "Terms and Conditions ", attributes: att2)
    attString2.addAttribute(NSForegroundColorAttributeName, value: Colors.loginButtonColor, range: NSMakeRange(0, attString2.length))

    let attString3 = NSMutableAttributedString(string: "and ", attributes: att1)
    attString3.addAttribute(NSForegroundColorAttributeName, value: UIColor.darkGrayColor(), range: NSMakeRange(0, attString3.length))

    let attString4 = NSMutableAttributedString(string: "Private Policy.", attributes: att2)
    attString4.addAttribute(NSForegroundColorAttributeName, value: Colors.loginButtonColor, range: NSMakeRange(0, attString4.length))

    let index = "\(attString1.string.endIndex)"

    attString1.insertAttributedString(attString4, atIndex: Int(index)!)
    attString1.insertAttributedString(attString3, atIndex: Int(index)!)
    attString1.insertAttributedString(attString2, atIndex: Int(index)!)

    termsAndConditionLabel.attributedText = attString1

I want the turqoise parts to be able to take the user to either Terms and Conditions, or Private Policy. Can anyone please help me with this problem? :))


Solution

  • I once made the same thing, but used textView instead of UILabel, you should create your own attributes, and add them to attributed string, and then handle tap, here some code

    your code with my modifications

       let regularFont = UIFont(name: "HelveticaNeue", size: 13)!
        let att1 = [NSFontAttributeName: regularFont]
    
        let turqoiseFont = UIFont(name: "HelveticaNeue", size: 13)!
        let att2 = [NSFontAttributeName: turqoiseFont]
        let mattString : NSMutableAttributedString  = NSMutableAttributedString()
        let attString1 = NSMutableAttributedString(string: "By creating a profile, I accept ", attributes: att1)
        attString1.addAttribute(NSForegroundColorAttributeName, value: UIColor.darkGray, range: NSMakeRange(0, attString1.length))
        let attString2 = NSMutableAttributedString(string: "Terms and Conditions ", attributes: att2)
        attString2.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSMakeRange(0, attString2.length))
        let attString3 = NSMutableAttributedString(string: "and ", attributes: att1)
        attString3.addAttribute(NSForegroundColorAttributeName, value: UIColor.darkGray, range: NSMakeRange(0, attString3.length))
        let attString4 = NSMutableAttributedString(string: "Private Policy.", attributes: att2)
        attString4.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSMakeRange(0, attString4.length))
        mattString.append(attString1)
        mattString.append(attString2)
        mattString.append(attString3)
        mattString.append(attString4)
        let attributes = ["link" : "termsLink"]
        let attributes2 = ["link" : "policyLink"]
        let str : NSString = mattString.string as NSString
        mattString.addAttributes(attributes, range: str.range(of: "Terms and Conditions"))
        mattString.addAttributes(attributes2, range: str.range(of: "Private Policy"))
        _textView.attributedText = mattString
        _textView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.singleTap(tap:)))) 
    

    and func to handle tap

    func singleTap(tap : UITapGestureRecognizer) {
        let layoutManager = _textView.layoutManager
        let location = tap.location(in: _textView)
        let index = layoutManager.characterIndex(for: location, in: _textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        if index > _textView.textStorage.length {return}
        var range : NSRange = NSRange()
        if let type = _textView.attributedText.attribute("link", at: index, effectiveRange: &range) as? String {
            if type == "termsLink" {
                //.. do smth
            } else {
                //.. do smth
            }
        }
    }
    

    UPDATE:

    here is the class I wrote to use Label

    https://github.com/barbados88/TapableLabel/blob/master/TapableLabel.swift