Wie kann ich konvertieren NSRange
zu Range<String.Index>
in Swift?
Ich möchte die folgende UITextFieldDelegate
Methode verwenden:
func textField(textField: UITextField!,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String!) -> Bool {
textField.text.stringByReplacingCharactersInRange(???, withString: string)
Antworten:
Die
NSString
Version (im Gegensatz zu Swift String) vonreplacingCharacters(in: NSRange, with: NSString)
einem übernimmtNSRange
, so eine einfache Lösung ist , zu konvertieren ,String
umNSString
ersten . Die Namen der Delegaten- und Ersetzungsmethoden unterscheiden sich in Swift 3 und 2 geringfügig. Je nachdem, welchen Swift Sie verwenden:Swift 3.0
Swift 2.x.
quelle
textField
Unicode-Zeichen mit mehreren Codeeinheiten wie Emoji enthalten sind. Da es sich um ein Eingabefeld handelt, kann ein Benutzer durchaus Emoji (😂) eingeben, andere Zeichen auf Seite 1 von Zeichen mit mehreren Codeeinheiten wie Flags (🇪🇸).UITextFieldDelegate
dietextField:shouldChangeCharactersInRange:replacementString:
Methode keinen Bereich bereitstellt, der innerhalb eines Unicode-Zeichens beginnt oder endet, da der Rückruf darauf basiert, dass ein Benutzer Zeichen über die Tastatur eingibt und nicht nur ein Teil eingegeben werden kann eines Emoji-Unicode-Zeichens. Beachten Sie, dass der Delegat dasselbe Problem haben würde, wenn er in Obj-C geschrieben wäre.(textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
Ab Swift 4 (Xcode 9) bietet die Swift-Standardbibliothek Methoden zum Konvertieren zwischen Swift-Zeichenfolgenbereichen (
Range<String.Index>
) undNSString
Bereichen (NSRange
). Beispiel:Daher kann die Textersetzung in der Textfelddelegatmethode jetzt wie folgt erfolgen
(Ältere Antworten für Swift 3 und früher :)
Ab Swift 1.2
String.Index
gibt es einen Initialisiererdie
NSRange
zurRange<String.Index>
korrekten Konvertierung in (einschließlich aller Fälle von Emojis, Regionalindikatoren oder anderen erweiterten Graphemclustern) ohne Zwischenkonvertierung in Folgendes verwendet werden kannNSString
:Diese Methode gibt einen optionalen Zeichenfolgenbereich zurück, da nicht alle
NSRange
s für eine bestimmte Swift-Zeichenfolge gültig sind.Die
UITextFieldDelegate
Delegate-Methode kann dann als geschrieben werdenDie inverse Umwandlung ist
Ein einfacher Test:
Update für Swift 2:
Die Swift 2-Version von
rangeFromNSRange()
wurde bereits von Serhii Yakovenko in dieser Antwort angegeben. Der Vollständigkeit halber füge ich sie hier ein:Die Swift 2 Version von
NSRangeFromRange()
istUpdate für Swift 3 (Xcode 8):
Beispiel:
quelle
range
ist(0,2)
daNSRange
bezieht sich auf die UTF-16 - Zeichen in einNSString
. Es zählt jedoch als ein Unicode-Zeichen in einer Swift-Zeichenfolge. -let end = advance(start, range.length)
Aus der referenzierten Antwort stürzt in diesem Fall mit der Fehlermeldung "Schwerwiegender Fehler: EndIndex kann nicht inkrementiert werden" ab.String
, um zur API-Referenz zu springen, alle Methoden und Kommentare zu lesen und dann verschiedene Dinge auszuprobieren, bis es funktioniert :)let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex)
ich denke, Sie wollen es tatsächlich einschränken durchutf16.endIndex - 1
. Andernfalls können Sie das Ende der Zeichenfolge beginnen.Sie müssen
Range<String.Index>
anstelle des Klassikers verwendenNSRange
. Die Art und Weise, wie ich es mache (vielleicht gibt es einen besseren Weg), besteht darin, die Saite zu nehmen und sie zuString.Index
bewegenadvance
.Ich weiß nicht, welchen Bereich Sie ersetzen möchten, aber tun wir so, als wollten Sie die ersten beiden Zeichen ersetzen.
quelle
Diese Antwort von Martin R scheint richtig zu sein, da sie Unicode ausmacht.
Zum Zeitpunkt des Posts (Swift 1) wird sein Code jedoch nicht in Swift 2.0 (Xcode 7) kompiliert, da die
advance()
Funktion entfernt wurde . Die aktualisierte Version ist unten:Swift 2
Swift 3
Swift 4
quelle
Dies ist vergleichbar mit Emilie Antwort jedoch , da Sie speziell gefragt , wie die zu konvertieren ,
NSRange
umRange<String.Index>
Sie so etwas wie dies tun würden:quelle
NSRange
spricht, obwohl ihre Komponenten Ganzzahlen sind) für die Zeichenansicht der Zeichenfolge, die möglicherweise fehlschlägt, wenn sie für eine Zeichenfolge mit "Zeichen" verwendet wird, für deren Ausdruck mehr als eine UTF16-Einheit erforderlich ist.Ein Riff über die großartige Antwort von @Emilie, keine Ersatz- / Konkurrenzantwort.
(Xcode6-Beta5)
Ausgabe:
Beachten Sie, dass die Indizes mehrere Codeeinheiten berücksichtigen. Das Flag (REGIONAL INDICATOR SYMBOL LETTERS ES) beträgt 8 Bytes und das (FACE WITH TEARS OF JOY) 4 Bytes. (In diesem speziellen Fall stellt sich heraus, dass die Anzahl der Bytes für UTF-8-, UTF-16- und UTF-32-Darstellungen gleich ist.)
Wickeln Sie es in eine Funktion:
Ausgabe:
quelle
quelle
In Swift 2.0 unter der Annahme
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
:quelle
Hier ist meine größte Anstrengung. Dies kann jedoch keine falschen Eingabeargumente überprüfen oder erkennen.
Ergebnis.
Einzelheiten
NSRange
vonNSString
Zählungen UTF-16 Code-Einheit s. UndRange<String.Index>
von SwiftString
ist ein undurchsichtiger relativer Typ, der nur Gleichheits- und Navigationsoperationen bietet. Dies ist absichtlich verstecktes Design.Obwohl die
Range<String.Index>
scheinbar auf den UTF-16-Code-Unit-Offset abgebildet sind, ist dies nur ein Implementierungsdetail, und ich konnte keine Erwähnung für eine Garantie finden. Das heißt, die Implementierungsdetails können jederzeit geändert werden. Die interne Darstellung von SwiftString
ist nicht genau definiert, und ich kann mich nicht darauf verlassen.NSRange
Werte können direktString.UTF16View
Indizes zugeordnet werden. Aber es gibt keine Methode, um es umzuwandelnString.Index
.Swift
String.Index
ist ein Index zum Iterieren von SwiftCharacter
, einem Unicode-Graphemcluster . Dann müssen SieNSRange
das richtige angeben, das die richtigen Graphemcluster auswählt. Wenn Sie wie im obigen Beispiel einen falschen Bereich angeben, wird ein falsches Ergebnis erzielt, da der richtige Graphemclusterbereich nicht ermittelt werden konnte.Wenn es eine Garantie dafür gibt, dass
String.Index
es sich um einen UTF-16-Code-Unit-Offset handelt, wird das Problem einfach. Aber es ist unwahrscheinlich, dass es passiert.Inverse Umwandlung
Auf jeden Fall kann die inverse Umwandlung genau durchgeführt werden.
Ergebnis.
quelle
return NSRange(location: a.utf16Count, length: b.utf16Count)
muss zureturn NSRange(location: a.utf16.count, length: b.utf16.count)
Ich habe festgestellt, dass die sauberste Lösung von swift2 nur darin besteht, eine Kategorie in NSRange zu erstellen:
Rufen Sie es dann für die Textfelddelegatfunktion auf:
quelle
Die offizielle Beta-Dokumentation zu Swift 3.0 bietet die Standardlösung für diese Situation unter dem Titel String.UTF16View im Abschnitt UTF16View Elements Match NSString Characters title
quelle
In der akzeptierten Antwort finde ich die Optionen umständlich. Dies funktioniert mit Swift 3 und scheint kein Problem mit Emojis zu haben.
quelle
quelle