Create tap-able “links” in the NSAttributedString of a UILabel?

前端 未结 30 3083
半阙折子戏
半阙折子戏 2020-11-22 02:07

I have been searching this for hours but I\'ve failed. I probably don\'t even know what I should be looking for.

Many applications have text and in this text are web

30条回答
  •  甜味超标
    2020-11-22 03:09

    Modified @timbroder code to handle multiple line correctly for swift4.2

    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: 0 );
            // Adjust for multiple lines of text
            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)
            var newTargetRange = targetRange
            if lineModifier > 0 {
                newTargetRange.location = targetRange.location+(lineModifier*Int(ceil(locationOfTouchInLabel.y)))
            }
            return NSLocationInRange(adjustedRange, newTargetRange)
        }
    }
    

    UILabel Code

    let tapAction = UITapGestureRecognizer(target: self, action: #selector(self.tapLabel(gesture:)))
    
    let quote = "For full details please see our privacy policy and cookie policy."
    let attributedString = NSMutableAttributedString(string: quote)
    
    let string1: String = "privacy policy", string2: String = "cookie policy"
    
    // privacy policy
    let rangeString1 = quote.range(of: string1)!
    let indexString1: Int = quote.distance(from: quote.startIndex, to: rangeString1.lowerBound)
    attributedString.addAttributes(
                [.font: ,
                 .foregroundColor: ,
                 .underlineStyle: 0, .underlineColor:UIColor.clear
            ], range: NSRange(location: indexString1, length: string1.count));
    
    // cookie policy
    let rangeString2 = quote.range(of: string2)!
    let indexString2: Int = quote.distance(from: quote.startIndex, to: rangeString2.lowerBound )
    
    attributedString.addAttributes(
                [.font: ,
                 .foregroundColor: ,
                 .underlineStyle: 0, .underlineColor:UIColor.clear
            ], range: NSRange(location: indexString2, length: string2.count));
    
    let label = UILabel()
    label.frame = CGRect(x: 20, y: 200, width: 375, height: 100)
    label.isUserInteractionEnabled = true
    label.addGestureRecognizer(tapAction)
    label.attributedText = attributedString
    
    

    Code to recognise the Tap

     @objc
      func tapLabel(gesture: UITapGestureRecognizer) {
         if gesture.didTapAttributedTextInLabel(label: , inRange: termsLabelRange {
                print("Terms of service")
         } else if gesture.didTapAttributedTextInLabel(label: inRange: privacyPolicyLabelRange) {
                print("Privacy policy")
         } else {
                print("Tapped none")
         }
        }
    

提交回复
热议问题