SwiftのString
範囲とNSString
範囲は「互換性がありません」。たとえば、😄のような絵文字は1つのSwift文字として数えられますが、2つの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)
出力:
😄😄😄ロングパラグラー{
} phは言う{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ing!{
}
ご覧のとおり、「ph say」は「saying」ではなく属性でマークされています。
以来 NS(Mutable)AttributedString
、最終的に必要となるNSString
とNSRange
、に与えられた文字列を変換するために、実際に優れているNSString
最初の。次に、これsubstringRange
はNSRange
範囲であり、範囲を変換する必要はありません。
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)
出力:
😄😄😄長い段落{
}言う{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}!{
}
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)
ここsubstringRange
でRange<String.Index>
、それは対応に変換されNSRange
て
NSRange(substringRange, in: text)