2017-03-18 99 views
0

我有一個標籤,我有正常的文本和標籤。我想要的#標籤有不同的顏色,所以我做了這樣的事情:NSAttributedString中的顏色標籤

let text = "#hellothere I am you. #hashtag #hashtag2 @you #hashtag3" 
     let words = text.components(separatedBy: " ") 
     let attribute = NSMutableAttributedString.init(string: text) 
     for word in words { 
      let range = (text as NSString).range(of: word) 
      if word.hasPrefix("#") { 
       attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: range) 
      } 
     } 
     label.attributedText = attribute 

然而,當我使用這個邏輯,結果不一致。有時,標籤會按照預期着色,其他時候包含標籤的單詞的某些部分會與下一個單詞的某個部分一起着色。我正在尋找一種解決方案,在該解決方案中,我可以識別單詞中是否存在「#」字符(僅字中第一次出現),然後將該單詞從哈希標籤着色至下一個空格,逗號,分號,句號或以字符串結尾,以適用者爲準。

回答

1

這需要一個正則表達式。 Swift 3中的示例。

let attrStr = NSMutableAttributedString(string: "#hellothere I am you. #hashtag, #hashtag2 @you #hashtag3") 
let searchPattern = "#\\w+" 
var ranges: [NSRange] = [NSRange]() 

let regex = try! NSRegularExpression(pattern: searchPattern, options: []) 
ranges = regex.matches(in: attrStr.string, options: [], range: NSMakeRange(0, attrStr.string.characters.count)).map {$0.range} 

for range in ranges { 
    attrStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellow, range: NSRange(location: range.location, length: range.length)) 
} 

attrStr 

將上述內容粘貼到操場上並查看輸出。要查看正在使用的正則表達式,請嘗試這個link

+0

工作!謝謝! – StudentX

0

我現在不能測試這個,但想到一個想法; 當你在你的example-string中搜索字符串#hashtag時,你不覺得它會找到三個位置,並且這可能與它有關嗎?也許不是那個特定的例子字符串,但假設你有字符串"#hellothere #hashtag2 #hashtag #hashtag3",並執行你的代碼。 當您循環到字符串"#hashtag"並使用text.range(of:"#hashtag")時,它將首先找到字符串"#hashtag2"。我不確定它是否會繼續尋找多個匹配項,但是使用您的代碼,"#hashtag2"同樣正確,因爲它包含"#hashtag"

我不知道解決這個問題的最佳方法,但我有一些想法。 一個想法可能是以某種方式在您要查找的單詞前後存儲該字母。因此,當您使用text.range(of:string)時,您不僅要查找"#hashtag",還要查找" #hashtag "。 僅僅在字符串之前和之後添加一個空格是不夠的,因爲它可能已經被另一個符號(逗號或句點等)分隔。

一個更好的解決方案可能是總結字符串的以前的部分,並且「手動」計算您的字符串的範圍,而不是使用text.range(of:string)

不是真正的代碼,而是試着去了解我在想什麼:

let text = "#hellothere I am you. #hashtag #hashtag2 @you #hashtag3" 
let words = text.components(separatedBy: " ") 
let attribute = NSMutableAttributedString.init(string: text) 
for (index, word) in words.enumerated() { 
    if word.hasPrefix("#") { 
     //Recreate the original string up to this part, so you can find the actual start-index of your current word. 
     let sentenceBeforeWord = words[0..<index].joined(separator: " ") 
     let range = // 
     attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: range) 
    } 
} 
label.attributedText = attribute 

然後計算你的話(#hashtag)自己的範圍。現在你確切知道你的單詞何時開始(它的startIndex應該是sentenceBeforeWord之後的一個字母,我認爲你的單詞的開始索引應該等於sentenceBeforeWord的長度)。而且你已經知道你的單詞的長度。 我不知道如何從頭頂創建NSRange(NSString) - 或Range(String) - 對象,但您可能會弄清楚。 您可能還需要修復我的循環中的一些邏輯,因爲我不知道words[0..<index]將在第一個循環中執行什麼操作(0 .. < 0)。

旁註,當您創建NSRange - 和/或Range-對象時要小心。一個字母並不總是一個字母。如果我正確記得,假設字符串範圍的長度等於字符數量並不總是正確的,因爲某些字符(例如表情符號)與常規字母數字字母具有不同的範圍和字符系統。一個表情符號可能是一個字母,但我相信它的範圍是兩個。

+0

@schmubob的正則表達式解決方案對我來說工作得很好..但是,我也會試試這個:) – StudentX

0

你的代碼似乎工作正常,你能提供一個例子,它不工作?

有一對夫婦的小改進,但是你可以做:

let text: NSString = "#hellothere I am you. #hashtag #hashtag2 @you #hashtag3" 
let words = text.components(separatedBy: " ").filter { $0.hasPrefix("#") } 
let attributed = NSMutableAttributedString(string: text as String) 
for word in words { 
    attributed.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: text.range(of: word)) 
} 
  1. 沒有必要調用NSMutableAttributedString.init(string: text)具體情況,你可以使用NSMutableAttributedString(string: text)
  2. 可以使用陣列上過濾器,以減少它直接忽略了在for循環期間檢查hashtag的需要。
  3. 我會建議將字符串聲明爲NSString,這樣就不需要在每次迭代中進行轉換 - 但是您需要將它轉換爲NSMutableAttributedString初始化。