NSAttributedString

时间:2017-03-18 09:50:11

标签: ios 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

但是,当我使用此逻辑时,结果不一致。有时,主题标签会按预期变色,有时候包含标签的单词的某些部分会与下一个单词的某些部分一起变色。我正在寻找一个解决方案,我可以确定单词中是否有“#”字符(仅在单词中出现第一个),然后从主题标签中着色,直到下一个空格,逗号,分号,句号或字符串的结尾,以适用者为准。

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

我现在无法对此进行测试,但想到了一个想法; 当您在示例字符串中搜索字符串#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之后的一个字母,I.E你的单词的startIndex应该等于sentenceBeforeWord的长度,我想)。你已经知道你的话的长度了。 我不知道如何从头顶创建NSRange(NSString) - 或Range(String) - 对象,但你可能能够弄明白。 你可能还需要修复我的循环中的一些逻辑,因为我不知道words[0..<index]在第一个循环中会做什么(0 ..&lt; 0)。

旁注,在创建NSRange - 和/或Range - 对象时要小心。一封信并不总是一个字母。如果我没记错的话,假设字符串范围的长度等于字符数并不总是正确的,因为某些字符(例如表情符号)具有与常规字母数字字母不同的范围和字符系统。一个表情符号可能是一个字母,但我相信它的范围是两个。

答案 2 :(得分: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循环期间检查主题标签的需要。
  3. 我建议将字符串声明为NSString,这样你就不需要在每次迭代中强制转换它 - 但是你需要将它转换为NSMutableAttributedString初始化。