Reputation: 1147
I'm setting the text on a textview and then calling this method of the UITextView extension in order to create links out of the words that are hashtags and mentions (Swift 3)
extension UITextView {
func resolveHashTags(font: UIFont = UIFont.systemFont(ofSize: 17.0)){
if let text = self.text {
let words:[String] = text.components(separatedBy: " ")
let attrs = [ NSFontAttributeName : font ]
let attrString = NSMutableAttributedString(string: text, attributes:attrs)
for word in words {
if (word.characters.count > 1 && ((word.hasPrefix("#") && word[1] != "#") || (word.hasPrefix("@") && word[1] != "@"))) {
let matchRange = text.range(of: word)
let newWord = String(word.characters.dropFirst())
if let matchRange = matchRange {
attrString.addAttribute(NSLinkAttributeName, value: "\(word.hasPrefix("#") ? "hashtag:" : "mention:")\(newWord)", range: text.NSRangeFromRange(range: matchRange))
}
}
}
self.attributedText = attrString
}
}
}
My issue is very simple. I have no way to create a link for something like this "helloworld#hello"
simply because my word does not have a prefix of "#"
Another scenario I can't figure out is when the user puts multi hashtags together for example "hello world, how are you? #success#moments#connect"
as this would all be considered 1 hashtag with the current logic when it should be 3 different links.
How do i correct? thank you
Upvotes: 1
Views: 1604
Reputation: 803
func resolveHashTags(text : String) -> NSAttributedString{
var length : Int = 0
let text:String = text
let words:[String] = text.separate(withChar: " ")
let hashtagWords = words.flatMap({$0.separate(withChar: "#")})
let attrs = [NSFontAttributeName : UIFont.systemFont(ofSize: 17.0)]
let attrString = NSMutableAttributedString(string: text, attributes:attrs)
for word in hashtagWords {
if word.hasPrefix("#") {
let matchRange:NSRange = NSMakeRange(length, word.characters.count)
let stringifiedWord:String = word
attrString.addAttribute(NSLinkAttributeName, value: "hash:\(stringifiedWord)", range: matchRange)
}
length += word.characters.count
}
return attrString
}
To separate words I used a string Extension
extension String {
public func separate(withChar char : String) -> [String]{
var word : String = ""
var words : [String] = [String]()
for chararacter in self.characters {
if String(chararacter) == char && word != "" {
words.append(word)
word = char
}else {
word += String(chararacter)
}
}
words.append(word)
return words
}
}
func textViewDidChange(_ textView: UITextView) {
textView.attributedText = resolveHashTags(text: textView.text)
textView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.red]
}
I hope this is what you are looking for. Tell me if it worked out for you.
Upvotes: 2
Reputation: 2307
If you're willing to use a 3rd party library, you could try using Mustard (disclaimer: I'm the author).
You could match hashtag tokens using this tokenizer:
struct HashtagTokenizer: TokenizerType, DefaultTokenizerType {
// start of token is identified by '#'
func tokenCanStart(with scalar: UnicodeScalar) -> Bool {
return scalar == UnicodeScalar(35) // ('35' is scalar value for the # character)
}
// all remaining characters must be letters
public func tokenCanTake(_ scalar: UnicodeScalar) -> Bool {
return CharacterSet.letters.contains(scalar)
}
}
Which can then be used to match the hashtags in the text:
let hashtags = "hello world, how are you? #success#moments#connect".tokens(matchedWith: HashtagTokenizer())
// hashtags.count -> 3
// hashtags[0].text -> "#success"
// hashtags[0].range -> 26..<34
// hashtags[1].text -> "#moments"
// hashtags[1].range -> 34..<42
// hashtags[2].text -> "#connect"
// hashtags[2].range -> 42..<50
The array returned is an array of tokens, where each one contains a text
property of the matched text, and a range
property of the range of that matched text in the original string that you can use to create a link on the text view.
Upvotes: 1