[ios] 스위프트 범위에서 NSRange?
문제 : Range를 사용하는 Swift String을 사용하는 동안 NSAttributedString이 NSRange를 사용합니다.
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)
}
})
다음과 같은 오류가 발생합니다.
오류 : ‘Range’는 ‘NSRange’로 변환 할 수 없습니다 trustedString.addAttribute (NSForegroundColorAttributeName, 값 : NSColor.redColor (), 범위 : substringRange)
답변
스위프트 String
범위와 NSString
범위는 “호환되지 않습니다”. 예를 들어, ?와 같은 이모지는 하나의 스위프트 문자로 간주되지만 두 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 말 { NSColor = "NSCalibratedRGBColorSpace 10010 1"; } ing! { }
보시다시피 “ph say”는 “saying”이 아닌 속성으로 표시되었습니다.
NS(Mutable)AttributedString
궁극적으로 NSString
and가 필요하기 때문에 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 10010 1"; }! { }
스위프트 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)
스위프트 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)
스위프트 4 업데이트 :
스위프트 4 (엑스 코드 9)로, 스위프트 표준 라이브러리 사이의 변환 방법을 제공 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
A는 Range<String.Index>
, 그리고 그 대응으로 변환된다 NSRange
으로
NSRange(substringRange, in: text)
답변
당신이 묘사 한 것과 같은 경우에는 이것이 효과가 있음을 알았습니다. 비교적 짧고 달콤합니다.
let attributedString = NSMutableAttributedString(string: "follow the yellow brick road") //can essentially come from a textField.text as well (will need to unwrap though)
let text = "follow the yellow brick road"
let str = NSString(string: text)
let theRange = str.rangeOfString("yellow")
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: theRange)
답변
대답은 괜찮지 만 Swift 4를 사용하면 코드를 약간 단순화 할 수 있습니다.
let text = "Test string"
let substring = "string"
let substringRange = text.range(of: substring)!
let nsRange = NSRange(substringRange, in: text)
range
기능 의 결과는 풀어야하므로주의하십시오 .
답변
가능한 해결책
Swift는 NSRange를 만드는 데 사용할 수있는 시작과 끝 사이의 거리를 측정하는 distance ()를 제공합니다.
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
let start = distance(text.startIndex, substringRange.startIndex)
let length = distance(substringRange.startIndex, substringRange.endIndex)
let range = NSMakeRange(start, length)
// println("word: \(substring) - \(d1) to \(d2)")
if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
}
})
답변
나를 위해 이것은 완벽하게 작동합니다.
let font = UIFont.systemFont(ofSize: 12, weight: .medium)
let text = "text"
let attString = NSMutableAttributedString(string: "exemple text :)")
attString.addAttributes([.font: font], range:(attString.string as NSString).range(of: text))
label.attributedText = attString
답변
스위프트 4 :
물론 Swift 4에 NSRange의 확장 기능이 이미 있다는 것을 알고 있습니다.
public init<R, S>(_ region: R, in target: S) where R : RangeExpression,
S : StringProtocol,
R.Bound == String.Index, S.Index == String.Index
나는 대부분의 경우이 init로 충분하다는 것을 알고있다. 사용법을보십시오 :
let string = "Many animals here: ??? !!!"
if let range = string.range(of: "???"){
print((string as NSString).substring(with: NSRange(range, in: string))) // "???"
}
그러나 Swift의 String 인스턴스없이 Range <String.Index>에서 NSRange로 직접 변환 할 수 있습니다.
대상 매개 변수를 String 으로 요구하는 일반적인 초기화 사용법 대신 대상 문자열 이없는 경우 직접 변환을 만들 수 있습니다
extension NSRange {
public init(_ range:Range<String.Index>) {
self.init(location: range.lowerBound.encodedOffset,
length: range.upperBound.encodedOffset -
range.lowerBound.encodedOffset) }
}
또는 Range 자체에 대한 특수 확장을 만들 수 있습니다
extension Range where Bound == String.Index {
var nsRange:NSRange {
return NSRange(location: self.lowerBound.encodedOffset,
length: self.upperBound.encodedOffset -
self.lowerBound.encodedOffset)
}
}
용법:
let string = "Many animals here: ??? !!!"
if let range = string.range(of: "???"){
print((string as NSString).substring(with: NSRange(range))) // "???"
}
또는
if let nsrange = string.range(of: "???")?.nsRange{
print((string as NSString).substring(with: nsrange)) // "???"
}
스위프트 5 :
Swift 문자열을 기본적으로 UTF-8 인코딩으로 마이그레이션하기 때문에 사용법은 encodedOffset
더 이상 사용되지 않는 것으로 간주되며 범위를 문자열 자체가 없으면 NSRange로 변환 할 수 없습니다. 오프셋을 계산하려면 소스 문자열이 필요하기 때문에 UTF-8로 인코딩되며 오프셋을 계산하기 전에 UTF-16으로 변환해야합니다. 현재로서는 가장 좋은 방법은 generic init 을 사용하는 것 입니다.
답변
스위프트 4
두 가지 방법이 있다고 생각합니다.
1. NSRange (범위 : in)
2. NSRange (위치 :, 길이 🙂
샘플 코드 :
let attributedString = NSMutableAttributedString(string: "Sample Text 12345", attributes: [.font : UIFont.systemFont(ofSize: 15.0)])
// NSRange(range, in: )
if let range = attributedString.string.range(of: "Sample") {
attributedString.addAttribute(.foregroundColor, value: UIColor.orange, range: NSRange(range, in: attributedString.string))
}
// NSRange(location: , length: )
if let range = attributedString.string.range(of: "12345") {
attributedString.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset))
}