Bagaimana saya bisa mengonversi NSRange
ke Range<String.Index>
dalam Swift?
Saya ingin menggunakan UITextFieldDelegate
metode berikut :
func textField(textField: UITextField!,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String!) -> Bool {
textField.text.stringByReplacingCharactersInRange(???, withString: string)
Jawaban:
The
NSString
Versi (sebagai lawan Swift String) darireplacingCharacters(in: NSRange, with: NSString)
menerima sebuahNSRange
, jadi salah satu solusi sederhana adalah dengan mengkonversiString
keNSString
pertama . Nama metode delegasi dan penggantian sedikit berbeda dalam Swift 3 dan 2, jadi tergantung pada Swift mana yang Anda gunakan:Swift 3.0
Swift 2.x
sumber
textField
berisi karakter unicode unit multi-kode unit seperti emoji. Karena ini adalah bidang entri, pengguna dapat memasukkan emoji (π), halaman 1 karakter karakter multi-kode unit lainnya seperti flag (πͺπΈ).UITextFieldDelegate
'stextField:shouldChangeCharactersInRange:replacementString:
metode tidak akan menyediakan berbagai yang dimulai atau berakhir dalam karakter unicode, karena callback didasarkan pada pengguna memasukkan karakter dari keyboard, dan itu tidak mungkin untuk mengetik hanya sebagian dari karakter unicode emoji. Perhatikan bahwa jika delegasi ditulis dalam Obj-C, itu akan memiliki masalah yang sama.(textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
Pada Swift 4 (Xcode 9), perpustakaan standar Swift menyediakan metode untuk mengkonversi antara rentang string Swift (
Range<String.Index>
) danNSString
rentang (NSRange
). Contoh:Oleh karena itu penggantian teks dalam metode delegasi bidang teks sekarang dapat dilakukan sebagai
(Jawaban lama untuk Swift 3 dan sebelumnya :)
Pada Swift 1.2,
String.Index
memiliki penginisialisasiyang dapat digunakan untuk mengkonversi
NSRange
denganRange<String.Index>
benar (termasuk semua kasus Emoji, Indikator Regional atau cluster grapheme diperluas) tanpa konversi antara keNSString
:Metode ini mengembalikan rentang string opsional karena tidak semua
NSRange
s valid untuk string Swift yang diberikan.The
UITextFieldDelegate
metode delegasi kemudian dapat ditulis sebagaiKonversi terbalik adalah
Tes sederhana:
Pembaruan untuk Swift 2:
Versi Swift 2
rangeFromNSRange()
sudah diberikan oleh Serhii Yakovenko dalam jawaban ini , saya memasukkannya di sini untuk kelengkapan:Versi Swift 2
NSRangeFromRange()
adalahPembaruan untuk Swift 3 (Xcode 8):
Contoh:
sumber
range
itu(0,2)
karenaNSRange
merujuk ke karakter UTF-16 dalam sebuahNSString
. Tapi itu dianggap sebagai satu karakter Unicode dalam string Swift. -let end = advance(start, range.length)
dari jawaban yang direferensikan tidak crash dalam hal ini dengan pesan kesalahan "kesalahan fatal: tidak dapat meningkatkan endIndex".String
untuk melompat ke referensi API, baca semua metode dan komentar, lalu coba hal-hal yang berbeda sampai berhasil :)let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex)
, saya pikir Anda benar-benar ingin membatasiutf16.endIndex - 1
. Jika tidak, Anda dapat memulai dari akhir string.Anda harus menggunakan
Range<String.Index>
bukan yang klasikNSRange
. Cara saya melakukannya (mungkin ada cara yang lebih baik) adalah dengan mengambil string yangString.Index
bergerak dengannyaadvance
.Saya tidak tahu kisaran apa yang ingin Anda ganti, tetapi mari kita berpura-pura ingin mengganti 2 karakter pertama.
sumber
Jawaban oleh Martin R ini tampaknya benar karena ini menjelaskan Unicode.
Namun pada saat posting (Swift 1) kodenya tidak dikompilasi di Swift 2.0 (Xcode 7), karena mereka menghapus
advance()
fungsinya. Versi terbaru di bawah ini:Cepat 2
Cepat 3
Cepat 4
sumber
Ini mirip dengan jawaban Emilie namun karena Anda bertanya secara spesifik bagaimana mengonversinya
NSRange
menjadiRange<String.Index>
Anda akan melakukan sesuatu seperti ini:sumber
NSRange
berbicara, meskipun komponennya adalah bilangan bulat) terhadap tampilan karakter string, yang mungkin gagal ketika digunakan pada string dengan "karakter" yang membutuhkan lebih dari satu unit UTF16 untuk mengekspresikan.Riff pada jawaban hebat oleh @Emilie, bukan jawaban pengganti / bersaing.
(Xcode6-Beta5)
Keluaran:
Perhatikan akun indeks untuk beberapa unit kode. Bendera (REGIONAL INDICATOR SYMBOL LETTERS ES) adalah 8 byte dan (WAJAH DENGAN TEAR DARI SUKACITA) adalah 4 byte. (Dalam kasus khusus ini ternyata jumlah byte sama untuk representasi UTF-8, UTF-16 dan UTF-32.)
Membungkusnya dengan fungsi:
Keluaran:
sumber
sumber
Dalam Swift 2.0 dengan asumsi
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
:sumber
Inilah upaya terbaik saya. Tetapi ini tidak dapat memeriksa atau mendeteksi argumen input yang salah.
Hasil.
Detail
NSRange
dariNSString
jumlah unit kode UTF-16 s. DanRange<String.Index>
dari SwiftString
adalah jenis relatif buram yang hanya menyediakan operasi kesetaraan dan navigasi. Ini adalah desain yang sengaja disembunyikan.Meskipun
Range<String.Index>
tampaknya dipetakan ke offset unit kode UTF-16, itu hanya detail implementasi, dan saya tidak dapat menemukan menyebutkan tentang jaminan apa pun. Itu berarti detail implementasi dapat diubah kapan saja. Representasi internal SwiftString
tidak cukup didefinisikan, dan saya tidak dapat mengandalkannya.NSRange
nilai dapat langsung dipetakan keString.UTF16View
indeks. Tetapi tidak ada metode untuk mengubahnya menjadiString.Index
.Swift
String.Index
adalah indeks untuk beralih SwiftCharacter
yang merupakan cluster graphode Unicode . Kemudian, Anda harus menyediakan yang tepatNSRange
yang memilih cluster grapheme yang benar. Jika Anda memberikan rentang yang salah seperti contoh di atas, itu akan menghasilkan hasil yang salah karena rentang cluster grapheme yang tepat tidak dapat dipecahkan.Jika ada jaminan bahwa
String.Index
adalah UTF-16 kode unit offset, maka masalah menjadi sederhana. Tapi itu tidak mungkin terjadi.Konversi terbalik
Pokoknya konversi terbalik dapat dilakukan dengan tepat.
Hasil.
sumber
return NSRange(location: a.utf16Count, length: b.utf16Count)
harus diubah kereturn NSRange(location: a.utf16.count, length: b.utf16.count)
Saya telah menemukan satu-satunya solusi swift2 terbersih adalah membuat kategori di NSRange:
Dan kemudian memanggilnya dari untuk fungsi delegasi bidang teks:
sumber
Dokumentasi resmi Swift 3.0 beta telah memberikan solusi standar untuk situasi ini di bawah judul String.UTF16View di bagian UTF16View Elements Match NSString Characters title
sumber
Dalam jawaban yang diterima saya menemukan opsional yang rumit. Ini bekerja dengan Swift 3 dan tampaknya tidak punya masalah dengan emoji.
sumber
sumber