【问题标题】:Keep hyperlink to specific word in UITextView using attributedString使用 attributesString 将超链接保留到 UITextView 中的特定单词
【发布时间】:2021-02-09 09:34:35
【问题描述】:

我正在尝试实现一个可以在打字时处理主题标签的编辑器。

extension UITextView {

func resolveHashTags() {
    if self.text.isEmpty {
        let emptyString = NSMutableAttributedString(string: " ", attributes: [NSAttributedString.Key.foregroundColor: UIColor.black,
                                                                              NSAttributedString.Key.font: self.font!])
        self.attributedText = emptyString
        self.textColor = .black
        self.text = ""
        return
    }
    let cursorRange = selectedRange
    let nsText = NSString(string: self.text)
    let words = nsText.components(separatedBy: CharacterSet(charactersIn: "@#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted).filter({!$0.isEmpty})

    self.textColor = .black
    let attrString = NSMutableAttributedString()
    attrString.setAttributedString(self.attributedText)
    attrString.addAttributes([NSAttributedString.Key.foregroundColor : UIColor.black], range: nsText.range(of: self.text))
    
    var anchor: Int = 0

    for word in words {

        // found a word that is prepended by a hashtag!
        // homework for you: implement @mentions here too.
        let matchRange:NSRange = nsText.range(of: word as String, range: NSRange(location: anchor, length: nsText.length - anchor))
        anchor = matchRange.location + matchRange.length
        if word.hasPrefix("#") {

            // a range is the character position, followed by how many characters are in the word.
            // we need this because we staple the "href" to this range.
            

            // drop the hashtag
            let stringifiedWord = word.dropFirst()
            if let firstChar = stringifiedWord.unicodeScalars.first, NSCharacterSet.decimalDigits.contains(firstChar) {
                // hashtag contains a number, like "#1"
                // so don't make it clickable
            } else {
                // set a link for when the user clicks on this word.
                // it's not enough to use the word "hash", but you need the url scheme syntax "hash://"
                // note:  since it's a URL now, the color is set to the project's tint color
                attrString.addAttribute(NSAttributedString.Key.link, value: "hash:\(stringifiedWord)", range: matchRange)
            }

        } else if !word.hasPrefix("@") {
            
        }
    }
    self.attributedText = attrString
    self.selectedRange = cursorRange
}
}

这是我用来在 UITextView 中创建超链接的扩展。致电func textViewDidChange(_ textView: UITextView)

所以在输入任何以# 开头的单词时。它会打开超链接并将颜色变为蓝色。输入预期的单词后,如果按空格键,它会返回黑色文本。这是预期的行为。

但是如果你清除文本并将你的课程移回像这样的主题标签词

它也不断扩展超链接到下一个单词。

仅保留指向该单词的超链接的任何解决方案。在主题标签之后输入的任何内容都应该是普通文本

【问题讨论】:

  • // so don't make it clickable:然后去掉.link属性,以防万一?还是在else后面} else if !word.hasPrefix("@") { ... } else { removeLinkIfThereWasOne }?如果您的代码在哪里分析“asd”,它在哪里通过?那么它的属性是什么?
  • 这是唯一生成play textview的属性字符串
  • 我需要将其设为链接。因为我在其中存储了一些额外的属性,一旦用户键入所有内容,我就需要这些属性。
  • 在某些情况下,我需要将多个单词作为链接。我唯一的问题是,在第一次输入空格时,文本会恢复正常。但是回到链接的词,它会扩展它。
  • 我的意思是,当您开始重新输入时,它会将.link 保留为新文本,因此您需要将其删除。在所有 else 案例中,删除 .link 属性。所以在 else 中做,或者就在 attrString.setAttributedString(self.attributedText):attrString.removeAttribute(.link, range: _NSRange(location: 0, length: attrString.length)) 之后。

标签: ios swift uitextview nsattributedstring


【解决方案1】:

我终于明白了。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    var shouldReturn = true
    let selectedRange = textView.selectedRange
    let attributedText = NSMutableAttributedString(attributedString: textView.attributedText)
    if !text.isEmpty && text != " " {
        var userAttributes = [(NSAttributedString.Key, Any, NSRange)]()
        attributedText.enumerateAttribute(.link, in: _NSRange(location: 0, length: textView.text.count), options: .longestEffectiveRangeNotRequired) { (value, range, stop) in
            if let url = value as? String, url.hasPrefix("user:") {
                userAttributes.append((.link, value!, range))
            }
        }
        if let userLink = userAttributes.first(where: {$0.2.contains(range.location - 1)}) {
            attributedText.replaceCharacters(in: range, with: NSAttributedString(string: text, attributes: [NSAttributedString.Key.link : userLink.1, NSAttributedString.Key.font : textView.font as Any]))
            textView.attributedText = attributedText
            shouldReturn = false
        } else {
            attributedText.replaceCharacters(in: range, with: NSAttributedString(string: text, attributes: [NSAttributedString.Key.font : textView.font as Any]))
            textView.attributedText = attributedText
            textDidChange?(textView)
            shouldReturn = false
        }
        textView.selectedRange = _NSRange(location: selectedRange.location + text.count, length: 0)
        textViewDidChange(textView)
    }
    return shouldReturn
}

这样我可以控制更新单词之间的链接,并且它不会在之后扩展到新单词。

【讨论】:

    猜你喜欢
    • 2021-05-12
    • 1970-01-01
    • 2017-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多