[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궁극적으로 NSStringand가 필요하기 때문에 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)

여기서 substringRangeA는 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))
}

스크린 샷 :
여기에 이미지 설명을 입력하십시오