Search code examples
iosswiftswift3delegates

How to get multiple word click in a single label


I am new to swift and I want to get multiple clickable word and different tap gesture on each word

EX:- "Please read terms & condition and Privacy Policy Properly" I need to click on "terms & condition" and print("terms") and when clicked on "Privacy Policy " It should print("Privacy")

I tried some thing but not getting proper out put as expected

let txt = NSMutableAttributedString(string: labelCreateAccount.text!)
        let range = (labelCreateAccount.text! as NSString).range(of: "Term & Condition")
        let range1 = (labelCreateAccount.text! as NSString).range(of: "Privacy Policy")
        
        txt.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.red] , range: range)
        txt.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.red] , range: range1)
        labelCreateAccount.addGestureRecognizer(UITapGestureRecognizer(target:range, action: #selector(LabelTapAccount)))

        labelCreateAccount.addGestureRecognizer(UITapGestureRecognizer(target:range 1, action: #selector(LabelTapAccount)))

        labelCreateAccount.attributedText = txt
        labelCreateAccount.isUserInteractionEnabled = true


Solution

  • create extension UITapGestureRecognizer

    extension UITapGestureRecognizer {
    
        func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {
            
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: CGSize.zero)
            let textStorage = NSTextStorage(attributedString: label.attributedText!)
            
            layoutManager.addTextContainer(textContainer)
            textStorage.addLayoutManager(layoutManager)
            
            textContainer.lineFragmentPadding = 0.0
            textContainer.lineBreakMode = label.lineBreakMode
            textContainer.maximumNumberOfLines = label.numberOfLines
            let labelSize = label.bounds.size
            textContainer.size = labelSize
            
            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: 0 );
            
            let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1
            let rightMostFirstLinePoint = CGPoint(x: labelSize.width, y: 0)
            let charsPerLine = layoutManager.characterIndex(for: rightMostFirstLinePoint, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            
            let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
            let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine)
            
            return NSLocationInRange(adjustedRange, targetRange)
        }
        
    }
    
    extension Range where Bound == String.Index {
        var nsRange: NSRange {
            return NSRange(location: self.lowerBound.encodedOffset,
                           length: self.upperBound.encodedOffset -
                            self.lowerBound.encodedOffset)
        }
    }
    

    add TapGesture to target Label and on listener, check tapped on target string from attributedText:

    @objc private func settingTapped(_ sender: UITapGestureRecognizer) {
        guard let range = self.yourLabel.text?.range(of: "your Text")?.nsRange else {
            return
        }
        
        if sender.didTapAttributedTextInLabel(label: self.yourLabel, inRange: range) {
            // your work
        }
        
    }
    

    now set Tap on Label:

    let txt = NSMutableAttributedString(string: labelCreateAccount.text!)
                let range = (labelCreateAccount.text! as NSString).range(of: "Term & Condition")
                let range1 = (labelCreateAccount.text! as NSString).range(of: "Privacy Policy")
                
                txt.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.red] , range: range)
                txt.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.red] , range: range1)
    
                labelCreateAccount.attributedText = txt
                
                let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.settingTapped(_:)))
                self.labelCreateAccount.addGestureRecognizer(tap)
                self.labelCreateAccount.isUserInteractionEnabled = true