将 GestureRecognizer 添加到 String 无法正常工作

Add GestureRecognizer to String not working properly

我有一个 Label,其中我有两个特定的 words,应该是 clickable。它是这样的:

NutungsbedigungenDatenschutzrichtlinien 应该都是 clickable。我为实现这一目标所做的是:

设置标签:

func setUpDocumentsLabel(){
    var textArray = [String]()
    var fontArray = [UIFont]()
    var colorArray = [UIColor]()
    textArray.append("Mit 'fortfahren' akzeptierst du die")
    textArray.append("Nutzungsbedingungen")
    textArray.append("und")
    textArray.append("Datenschutzrichtlinien.")

    fontArray.append(Fonts.regularFontWithSize(size: 13.0))
    fontArray.append(Fonts.boldFontWithSize(size: 13.0))
    fontArray.append(Fonts.regularFontWithSize(size: 13.0))
    fontArray.append(Fonts.boldFontWithSize(size: 13.0))

    colorArray.append(Colors.white)
    colorArray.append(Colors.white)
    colorArray.append(Colors.white)
    colorArray.append(Colors.white)

    self.documentsLabel.attributedText = getAttributedString(arrayText: textArray, arrayColors: colorArray, arrayFonts: fontArray)

    self.documentsLabel.isUserInteractionEnabled = true
    let tapgesture = UITapGestureRecognizer(target: self, action: #selector(tappedOnLabel(_ :)))
    tapgesture.numberOfTapsRequired = 1
    self.documentsLabel.addGestureRecognizer(tapgesture)
}

点击操作:

@objc func tappedOnLabel(_ gesture: UITapGestureRecognizer) {
    guard let text = self.documentsLabel.text else { return }
    let nutzen = (text as NSString).range(of: "Nutzungsbedingungen")
    let daten = (text as NSString).range(of: "Datenschutzrichtlinien")

    if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: nutzen) {

        let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Nutzungsbedingungen", preferredStyle: .alert)
        let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in

        }
        alertcontroller.addAction(alertAction)
        self.present(alertcontroller, animated: true)

    } else if gesture.didTapAttributedTextInLabel(label: self.documentsLabel, inRange: daten){

        let alertcontroller = UIAlertController(title: "Tapped on", message: "user tapped on Datenschutzrichtlinien", preferredStyle: .alert)
        let alertAction = UIAlertAction(title: "OK", style: .default) { (alert) in

        }
        alertcontroller.addAction(alertAction)
        self.present(alertcontroller, animated: true)

    }
}

getAttributedString:

func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {

    let finalAttributedString = NSMutableAttributedString()

    for i in 0 ..< (arrayText?.count)! {

        let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
        let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))

        if i != 0 {

            finalAttributedString.append(NSAttributedString.init(string: " "))
        }

        finalAttributedString.append(attributedStr)
    }

    return finalAttributedString
}

问题:

strings 不是每个字符都 clickableclickable区的length因设备而异:

对于Datenschutzrichtlinien

iPhone 11: clickable 来自 D - t

iPhone SE: clickable 来自 D - 2nd i

对于Nutzungsbedingungen

在每个设备上都很好,但 iPhone SE:仅来自 N - 2nd n

我不知道为什么会这样,如果有人能帮助我,我将不胜感激!

更新:

我正在为 UITapGestureRecognizer 使用这个 extension:

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)
}

你在这里遇到的具体问题是因为你的标签有

.textAlignment = .center

但是 didTapAttributedTextInLabel() 代码不知道...它根据从 [ 开始的“und Datenschutzrichtlinien.” 计算位置=30=]标签左边缘.

您可以尝试此方法来修复它 - 快速测试(仅在一台设备上)- 似乎可以解决问题。

在您的 FirstLaunchViewController class 中,不要将文档标签居中:

let documentsLabel: UILabel = {
    let v = UILabel()
    // don't do this
    //v.textAlignment = .center
    v.numberOfLines = 0
    v.translatesAutoresizingMaskIntoConstraints = false
    return v
}()

您似乎只使用了一次 func getAttributedString(...),那就是为 documentsLabel 设置属性文本的格式,因此请按如下方式更改它:

func getAttributedString(arrayText:[String]?, arrayColors:[UIColor]?, arrayFonts:[UIFont]?) -> NSMutableAttributedString {

    let finalAttributedString = NSMutableAttributedString()

    for i in 0 ..< (arrayText?.count)! {

        let attributes = [NSAttributedString.Key.foregroundColor: arrayColors?[i], NSAttributedString.Key.font: arrayFonts?[i]]
        let attributedStr = (NSAttributedString.init(string: arrayText?[i] ?? "", attributes: attributes as [NSAttributedString.Key : Any]))

        if i != 0 {

            finalAttributedString.append(NSAttributedString.init(string: " "))
        }

        finalAttributedString.append(attributedStr)
    }

    // add paragraph attribute
    let paragraph = NSMutableParagraphStyle()
    paragraph.alignment = .center
    let attributes: [NSAttributedString.Key : Any] = [NSAttributedString.Key.paragraphStyle: paragraph]
    finalAttributedString.addAttributes(attributes, range: NSRange(location: 0, length: finalAttributedString.length))

    return finalAttributedString
}

现在您的 didTapAttributedTextInLabel(...) 函数应该检测到相对于 "tappable" 单词的正确点击位置。