Как преобразовать Range в NSRange?

Я обновляю свой код в swift 3. Получаем ошибку при преобразовании Range в NSRange. Как это сделать в быстром 3?

func nsRange(_ range : Range<String.Index>) -> NSRange {
    let utf16from = String.UTF16View.Index(range.lowerBound, within: utf16)
    let utf16to   = String.UTF16View.Index(range.upperBound,   within: utf16)
    return NSRange(location: utf16.startIndex.distanceTo(utf16from), length: utf16from.distanceTo(utf16to))
}

Ответ 1

Xcode 10.2 • Swift 5

extension StringProtocol {
    func nsRange(from range: Range<Index>) -> NSRange {
        return .init(range, in: self)
    }
}

let string = "Hello USA 🇺🇸 !!! Hello World !!!"

if let range = string.range(of: "Hello World") {
    let nsRange = string.nsRange(from: range)
    (string as NSString).substring(with: nsRange) //  "Hello World"
}

Вы также можете создать соответствующий nsRange(of:) расширяющий StringProtocol:

extension StringProtocol {
    func nsRange(of string: Self, options: String.CompareOptions = [], range: Range<Index>? = nil, locale: Locale? = nil) -> NSRange? {
        guard let range = self.range(of: string, options: options, range: range ?? startIndex..<endIndex, locale: locale ?? .current) else { return nil }
        return .init(range, in: self)
    }
    func nsRanges(of string: Self, options: String.CompareOptions = [], range: Range<Index>? = nil, locale: Locale? = nil) -> [NSRange] {
        var start = range?.lowerBound ?? startIndex
        let end = range?.upperBound ?? endIndex
        var ranges: [NSRange] = []
        while start < end, let range = self.range(of: string, options: options, range: start..<end, locale: locale ?? .current) {
            ranges.append(.init(range, in: self))
            start = range.upperBound
        }
        return ranges
    }
}

let string = "Hello USA 🇺🇸 !!! Hello World !!!"

if let nsRange = string.nsRange(of: "Hello World") {
    (string as NSString).substring(with: nsRange) //  "Hello World"
}
let nsRanges = string.nsRanges(of: "Hello")
print(nsRanges)   // "[{0, 5}, {19, 5}]\n"

Ответ 2

Прежде всего, я хотел бы сказать большое спасибо автору этого предмета, некоторое время назад я использовал его решение для Swift 3. (я уже увеличил его репутацию)

но я бы предложил специализированное расширение для самого диапазона

в Swift 4

extension Range where Bound == String.Index {
    var nsRange:NSRange {
        return NSRange(location: self.lowerBound.encodedOffset,
                         length: self.upperBound.encodedOffset -
                                 self.lowerBound.encodedOffset)
    }
}

в этом случае предыдущий образец может выглядеть как

let string = "Hello USA 🇺🇸 !!! Hello World !!!"

if let nsrange = string.range(of: "Hello World")?.nsRange {
    (string as NSString).substring(with: nsrange) //  "Hello World"
}