ios - Swift Range 的 NSRange?

标签 ios macos swift nsrange

问题: NSAttributedString 采用 NSRange,而我使用的是使用 Range 的 Swift String

let text = "Long paragraph saying something goes here!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})

产生以下错误:

error: 'Range' is not convertible to 'NSRange' attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)

最佳答案

swift String范围和NSString范围不“兼容”。 例如,像 😄 这样的表情符号算作一个 Swift 字符,但算作两个 NSString 字符(所谓的 UTF-16 代理对)。

因此,如果字符串,您建议的解决方案将产生意外的结果 包含这样的字符。示例:

let text = "😄😄😄Long paragraph saying!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
    let start = distance(text.startIndex, substringRange.startIndex)
    let length = distance(substringRange.startIndex, substringRange.endIndex)
    let range = NSMakeRange(start, length)

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
    }
})
println(attributedString)

输出:

😄😄😄Long paragra{
}ph say{
    NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}ing!{
}

As you see, "ph say" has been marked with the attribute, not "saying".

Since NS(Mutable)AttributedString ultimately requires an NSString and an NSRange, it is actually better to convert the given string to NSString first. Then the substringRange is an NSRange and you don't have to convert the ranges anymore:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: nsText)

nsText.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
println(attributedString)

输出:

😄😄😄Long paragraph {
}saying{
    NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}!{
}

Update for Swift 2:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstringsInRange(textRange, options: .ByWords, usingBlock: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
print(attributedString)

Swift 3 更新:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstrings(in: textRange, options: .byWords, using: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.red, range: substringRange)
    }
})
print(attributedString)

Swift 4 更新:

从 Swift 4 (Xcode 9) 开始,Swift 标准库 提供在Range<String.Index>之间进行转换的方法和NSRange 。 转换为NSString不再需要:

let text = "😄😄😄Long paragraph saying!"
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstrings(in: text.startIndex..<text.endIndex, options: .byWords) {
    (substring, substringRange, _, _) in
    if substring == "saying" {
        attributedString.addAttribute(.foregroundColor, value: NSColor.red,
                                      range: NSRange(substringRange, in: text))
    }
}
print(attributedString)

这里substringRangeRange<String.Index> ,并将其转换为 对应NSRange

NSRange(substringRange, in: text)

关于ios - Swift Range 的 NSRange?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55022293/

相关文章:

iphone - UIScrollView 或 UITableView 中图像的平铺排列

ios - "lldb"测试内购错误

swift - 计算和存储具有多个属性的变量以便于检索的最佳方式

python - MacOSX 上带有 python 的 Sublime Text 3 -- 模块缺少其方法

ios - Swift 中 NSTimer 的问题

ios - UIImage 方面填充偏移

ios - 多次调用 block 显示 View 中的 UIAlertView

Xcode 相当于 ' __asm int 3/DebugBreak()/Halt?

swift - 快速播放顺序音频文件

ios - 调用 "didSelectItemAt"时从单元格内调用函数