Как преобразовать NSRange в Range<String.Index> в Swift для работы с NSString?
Проблема: разные типы индексов
В Swift строки (String) — это коллекции символов Unicode, и индексы у них не целочисленные, а основанные на графемах. NSRange из Objective-C использует целые числа (location и length), которые не подходят напрямую для Swift String. Поэтому нужно преобразовать NSRange в Range<String.Index>.
Решение: метод range(from:)
Самый простой способ — использовать метод Range<String.Index>? = String.range(from: NSRange), который доступен через мост с NSString. Но его нет напрямую у String. Вместо этого мы можем получить String.UTF16View или работать через NSString. Вот универсальный подход:
import Foundation
let nsString: NSString = "Привет, мир! Hello, world!"
let searchRange = nsString.range(of: "мир")
// Преобразуем NSRange в Range<String.Index>
if let swiftRange = Range<String.Index>(searchRange, in: nsString as String) {
let substring = (nsString as String)[swiftRange]
print(substring) // "мир"
} else {
print("Диапазон недействителен")
}
Объяснение
- Range<String.Index>(searchRange, in: string) — это инициализатор, который принимает NSRange и строку, для которой нужно создать диапазон. Он автоматически корректно обрабатывает многобайтовые символы (кириллица, эмодзи).
- Если
NSRangeвыходит за пределы строки, инициализатор вернётnil— поэтому используемif let. - Метод работает как с ASCII, так и с Unicode.
Пример с эмодзи
let emojiString: NSString = "😀👍🏼🔥"
let range = emojiString.range(of: "👍🏼")
if let swiftRange = Range(range, in: emojiString as String) {
let substring = (emojiString as String)[swiftRange]
print(substring) // "👍🏼"
}
Альтернативный способ: через UTF16View
Если вы работаете с чистой Swift String (без NSString), можно использовать utf16Offset:
let swiftString = "Привет, мир!"
let nsRange = NSRange(location: 8, length: 3)
if let start = String.Index(utf16Offset: nsRange.location, in: swiftString),
let end = String.Index(utf16Offset: nsRange.location + nsRange.length, in: swiftString) {
let substring = swiftString[start..<end]
print(substring) // "мир"
}
Когда это нужно?
- При интеграции с Objective-C кодом (UIKit, NSAttributedString, NSRegularExpression).
- При работе с текстовыми полями (UITextField, UITextView), где делегаты возвращают NSRange.
- При парсинге текста с помощью Foundation-методов.
Важно
Убедитесь, что NSRange получен из той же строки, которую вы преобразуете. Если строка была изменена, диапазон может стать недействительным. Всегда проверяйте результат через if let или guard let.
Прямое использование NSRange с NSString (если не нужна Swift String)
Если вам не обязательно преобразовывать результат в Swift String, вы можете остаться в мире NSString и использовать метод substring(with:):
let nsString: NSString = "Привет, мир! Hello, world!"
let searchRange = nsString.range(of: "мир")
if searchRange.location != NSNotFound {
let substring = nsString.substring(with: searchRange)
print(substring) // "мир"
}
Этот подход проще и быстрее, так как не требует преобразования типов. Он безопасен для многобайтовых символов, потому что NSString тоже работает с Unicode.
Когда использовать этот вариант?
- Когда вы работаете с Objective-C API, которые возвращают NSString.
- Если вам не нужны возможности Swift String (например, map, filter).
- В критичных по производительности участках (преобразование типов имеет накладные расходы).
Недостаток
Вы теряете преимущества Swift String: безопасность типов, поддержка протоколов Collection, Sequence и т.д. Если в дальнейшем вы будете активно манипулировать строкой (например, искать другие подстроки, изменять регистр), лучше преобразовать в Swift String с помощью первого ответа.
Ответов пока нет.
Войдите чтобы оставить ответ