简体   繁体   中英

How to make UITextView detect hashtags?

I know that the UITextView default can detect URL, but how can i make it detect hashtags(#)?

It doesn't needs to detect hashtags while typing, but then viewDidLoad the text is set in the UITextView , so i want to detect hashtags as a color or something.

I have been using ActiveLabel , but that is only for UILabel , and i need the scroll function that the UITextView has.

This should work for you

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

To use this you can do something like this:

self.textView.text = "This is an #example test"
self.textView.resolveHashTags()

Updated for 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
    }
}

One option would be to use an NSAttributedString , something like this...

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
}

Then assign your attributedText like this...

let myText = "some text with a #hashtag in side of it #itsnoteasy"
self.textView.attributedText = convertHashtags(myText)

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

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

Add UITextViewDelegate in your class and Use like this

 self.tv_yourTextView.delegate = self
 self.tv_yourTextView.attributedText = tv_yourTextView.convertHashtags(text: "This is an #museer test")

delegate funtion

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

-> Modified @Wez Answer

when to make mention or hashtag or any word, yo can make it by:

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
   }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM