如何使UITextView检测到标签?

11

我知道UITextView默认可以检测URL,但是如何让它检测hashtags(#)?

在输入时无需检测hashtags,但是当在viewDidLoad中设置了文本后,我想要将hashtags检测出来并以颜色或其他形式显示。

我一直在使用ActiveLabel,但那只适用于UILabel,而我需要UITextView的滚动功能。


1
你可以将 ActiveLabel 放在一个 UIScrollView 中。 - rckoenes
@rckoenes,您是指这里吗?:http://s27.postimg.org/v2gk43w43/Screen_Shot_2015_12_15_at_18_11_15.png - 这不起作用。 - Roduck Nickes
看看FocusKit,它可能会有所帮助。 - kye
5个回答

18

这对你应该有效

  1. Create a new swift file with any name(UITextViewHashtagExtension.swift)
  2. Insert this code below:

    import UIKit
    
    extension UITextView {
    
    func resolveHashTags(){
    
        // turn string in to NSString
        let nsText:NSString = self.text
    
        // this needs to be an array of NSString.  String does not work.
        let words:[NSString] = nsText.componentsSeparatedByString(" ")
    
        // you can't set the font size in the storyboard anymore, since it gets overridden here.
        let attrs = [
            NSFontAttributeName : UIFont.systemFontOfSize(17.0)
        ]
    
        // you can staple URLs onto attributed strings
        let attrString = NSMutableAttributedString(string: nsText as String, attributes:attrs)
    
        // tag each word if it has a hashtag
        for word in words {
    
            // found a word that is prepended by a hashtag!
            // homework for you: implement @mentions here too.
            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.
                let matchRange:NSRange = nsText.rangeOfString(word as String)
    
                // convert the word from NSString to String
                // this allows us to call "dropFirst" to remove the hashtag
                var stringifiedWord:String = word as String
    
                // drop the hashtag
                stringifiedWord = String(stringifiedWord.characters.dropFirst())
    
                // check to see if the hashtag has numbers.
                // ribl is "#1" shouldn't be considered a hashtag.
                let digits = NSCharacterSet.decimalDigitCharacterSet()
    
                if let numbersExist = stringifiedWord.rangeOfCharacterFromSet(digits) {
                    // 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(NSLinkAttributeName, value: "hash:\(stringifiedWord)", range: matchRange)
                }
    
            }
        }
    
        // we're used to textView.text
        // but here we use textView.attributedText
        // again, this will also wipe out any fonts and colors from the storyboard,
        // so remember to re-add them in the attrs dictionary above
        self.attributedText = attrString
    }
    
    }
    
要使用这个,你可以像下面这样做:
self.textView.text = "This is an #example test"
self.textView.resolveHashTags()

更新至Swift 4.0:

extension UITextView {

    func resolveHashTags() {

        // turn string in to NSString
        let nsText = NSString(string: self.text)

        // this needs to be an array of NSString.  String does not work.
        let words = nsText.components(separatedBy: CharacterSet(charactersIn: "#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted)

        // you can staple URLs onto attributed strings
        let attrString = NSMutableAttributedString()
        attrString.setAttributedString(self.attributedText)

        // tag each word if it has a hashtag
        for word in words {
            if word.count < 3 {
                continue
            }

            // found a word that is prepended by a hashtag!
            // homework for you: implement @mentions here too.
            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.
                let matchRange:NSRange = nsText.range(of: word as String)

                // 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(NSAttributedStringKey.link, value: "hash:\(stringifiedWord)", range: matchRange)
                }

            }
        }

        // we're used to textView.text
        // but here we use textView.attributedText
        // again, this will also wipe out any fonts and colors from the storyboard,
        // so remember to re-add them in the attrs dictionary above
        self.attributedText = attrString
    }
}

2
阿拉伯语的井号让应用崩溃了 :(( - Kegham K.
今天我了解到,阿拉伯语的标签与其他标签不同。 - Akaino
我能在运行时更改哈希标签的颜色吗? - Yogesh Patel
如果我的字符串包含类似于“#Yogesh how are you Yogesh”的内容,那么它会更改起始范围的颜色,因为在我的字符串中我添加了两次相同的字符Yogesh。有什么解决办法吗?谢谢。 - Yogesh Patel
如果您有多个相同的hashtag,此代码仅会检测第一个hashtag。例如:hello #hi how are you. #hi are you there? 第二个#hi不会被添加为链接。 - Mrugesh Tank

8
一种选择是使用 NSAttributedString,代码如下...
func convertHashtags(text:String) -> NSAttributedString {
    let attrString = NSMutableAttributedString(string: text)
    attrString.beginEditing()
    // match all hashtags
    do {
        // Find all the hashtags in our string
        let regex = try NSRegularExpression(pattern: "(?:\\s|^)(#(?:[a-zA-Z].*?|\\d+[a-zA-Z]+.*?))\\b", options: NSRegularExpressionOptions.AnchorsMatchLines)
        let results = regex.matchesInString(text,
            options: NSMatchingOptions.WithoutAnchoringBounds, range: NSMakeRange(0, text.characters.count))
        let array = results.map { (text as NSString).substringWithRange($0.range) }
        for hashtag in array {
            // get range of the hashtag in the main string
            let range = (attrString.string as NSString).rangeOfString(hashtag)
            // add a colour to the hashtag
            attrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor() , range: range)
        }
        attrString.endEditing()
    }
    catch {
        attrString.endEditing()
    }
    return attrString
}

然后像这样分配您的attributedText...
let myText = "some text with a #hashtag in side of it #itsnoteasy"
self.textView.attributedText = convertHashtags(myText)

谢谢!另一个快速问题:我如何检测对哈希标签的按下?当哈希标签被点击时执行某个操作。 - Roduck Nickes
3
我完全不知道。 - Wez
4
这个hashtag已经可以被点击了,因为我们将它添加到了链接“hash:hashtag”中。我们只需要加上“textView.delegate = self”,并监听函数“func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool”。(您如何格式化此代码?) - rcpfuchs

5

针对Swift 4.0版本

extension UITextView {

    func resolveHashTags() {

        // turn string in to NSString
        let nsText = NSString(string: self.text)

        // this needs to be an array of NSString.  String does not work.
        let words = nsText.components(separatedBy: CharacterSet(charactersIn: "#ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_").inverted)

        // you can staple URLs onto attributed strings
        let attrString = NSMutableAttributedString()
        attrString.setAttributedString(self.attributedText)

        // tag each word if it has a hashtag
        for word in words {
            if word.count < 3 {
                continue
            }

            // found a word that is prepended by a hashtag!
            // homework for you: implement @mentions here too.
            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.
                let matchRange:NSRange = nsText.range(of: word as String)

                // 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(NSAttributedStringKey.link, value: "hash:\(stringifiedWord)", range: matchRange)
                }

            }
        }

        // we're used to textView.text
        // but here we use textView.attributedText
        // again, this will also wipe out any fonts and colors from the storyboard,
        // so remember to re-add them in the attrs dictionary above
        self.attributedText = attrString
    }
}

3

针对Swift 3+

 extension UITextView {
        
        func convertHashtags(text:String) -> NSAttributedString {
            
            let attr = [
                NSFontAttributeName : UIFont.systemFont(ofSize: 17.0),
                NSForegroundColorAttributeName : clr_golden,    
                NSLinkAttributeName : "https://Laitkor.com"
             ] as [String : Any]

        
        let attrString = NSMutableAttributedString(string: text)
        attrString.beginEditing()
        // match all hashtags
        do {
            // Find all the hashtags in our string
            let regex = try NSRegularExpression(pattern: "(?:\\s|^)(#(?:[a-zA-Z].*?|\\d+[a-zA-Z]+.*?))\\b", options: NSRegularExpression.Options.anchorsMatchLines)
            let results = regex.matches(in: text,
                                                options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: NSMakeRange(0, text.characters.count))
            let array = results.map { (text as NSString).substring(with: $0.range) }
            for hashtag in array {
                // get range of the hashtag in the main string
                let range = (attrString.string as NSString).range(of: hashtag)
                // add a colour to the hashtag
                //attrString.addAttribute(NSForegroundColorAttributeName, value: clr_golden , range: range)
                attrString.addAttributes(attr, range: range)
            }
            attrString.endEditing()
        }
        catch {
            attrString.endEditing()
        }
        return attrString
    }
}

在你的类中添加UITextViewDelegate并按如下方式使用
 self.tv_yourTextView.delegate = self
 self.tv_yourTextView.attributedText = tv_yourTextView.convertHashtags(text: "This is an #museer test")

委托函数

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    print("hastag Selected!")
    return true
}

-> 修改自@Wez的答案


嗨,当我点击标签时,应该调用shouldInteractWith这个方法,但我只想要名称,它应该显示为hash:name。我怎么才能在这里只获取名称?谢谢。 - Yogesh Patel

0

当需要提及或使用hashtag或任何单词时,您可以通过以下方式实现:

func convertRegex(text:String, regex: String) -> NSAttributedString {

           let attr:[NSAttributedString.Key:Any] = [
            NSAttributedString.Key.font : UIFont.systemFont(ofSize: 17.0),
            NSAttributedString.Key.foregroundColor : UIColor.green,
            ] as [NSAttributedString.Key : Any]
   
       
       let attrString = NSMutableAttributedString(string: text)
       attrString.beginEditing()
       do {
           // Find all the specific word in our string
           let regex = try NSRegularExpression(pattern:  "\\s\(regex)\\b" , options: NSRegularExpression.Options.anchorsMatchLines)
           let results = regex.matches(in: text,
                                               options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: NSMakeRange(0, text.count))
           let array = results.map { (text as NSString).substring(with: $0.range) }
           for word in array {
               let range = (attrString.string as NSString).range(of: word)
               attrString.addAttributes(attr, range: range)
           }
           attrString.endEditing()
       }
       catch {
           attrString.endEditing()
       }
       return attrString
   }

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接