Comment puis - je convertir NSRange
à Range<String.Index>
Swift?
Je souhaite utiliser la UITextFieldDelegate
méthode suivante :
func textField(textField: UITextField!,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String!) -> Bool {
textField.text.stringByReplacingCharactersInRange(???, withString: string)
Réponses:
La
NSString
version (par opposition à Swift String)replacingCharacters(in: NSRange, with: NSString)
accepte unNSRange
, donc une solution simple est de convertirString
enNSString
premier . Les noms des méthodes de délégué et de remplacement sont légèrement différents dans Swift 3 et 2, donc selon le Swift que vous utilisez:Swift 3.0
Swift 2.x
la source
textField
contient des caractères unicode d'unité multi-code tels que des emoji. Puisqu'il s'agit d'un champ de saisie, un utilisateur peut très bien saisir des emoji (😂), d'autres caractères de la page 1 de caractères d'unité de code multiple tels que des drapeaux (🇪🇸).UITextFieldDelegate
latextField:shouldChangeCharactersInRange:replacementString:
méthode ne fournira pas une plage qui commence ou se termine à l'intérieur d'un caractère unicode, car le rappel est basé sur un utilisateur saisissant des caractères à partir du clavier, et il n'est pas possible de taper seulement une partie d'un caractère emoji unicode. Notez que si le délégué était écrit en Obj-C, il aurait ce même problème.(textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)
Au Swift 4 (Xcode 9), la bibliothèque standard Swift fournit des méthodes de conversion entre les plages de chaînes Swift (
Range<String.Index>
) et lesNSString
plages (NSRange
). Exemple:Par conséquent, le remplacement de texte dans la méthode de délégué de champ de texte peut maintenant être effectué comme
(Anciennes réponses pour Swift 3 et versions antérieures :)
Depuis Swift 1.2,
String.Index
possède un initialiseurqui peut être utilisé pour convertir
NSRange
àRange<String.Index>
correctement (y compris tous les cas de emoji, des indicateurs régionaux ou d' autres groupes de graphèmes étendus) sans conversion intermédiaire à unNSString
:Cette méthode renvoie une plage de chaînes facultative car toutes
NSRange
s ne sont pas valides pour une chaîne Swift donnée.La
UITextFieldDelegate
méthode déléguée peut alors être écrite commeLa conversion inverse est
Un test simple:
Mise à jour pour Swift 2:
La version Swift 2 de
rangeFromNSRange()
a déjà été donnée par Serhii Yakovenko dans cette réponse , je l'inclus ici pour être complet:La version Swift 2 de
NSRangeFromRange()
isMise à jour pour Swift 3 (Xcode 8):
Exemple:
la source
range
c'est(0,2)
parce qu'ilNSRange
fait référence aux caractères UTF-16 dans unNSString
. Mais il compte comme un caractère Unicode dans une chaîne Swift. - àlet end = advance(start, range.length)
partir de la réponse référencée se bloque dans ce cas avec le message d'erreur "erreur fatale: impossible d'incrémenter endIndex".String
pour accéder à la référence API, lisez toutes les méthodes et commentaires, puis essayez différentes choses jusqu'à ce que cela fonctionne :)let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex)
, je pense que vous voulez vraiment le limiter parutf16.endIndex - 1
. Sinon, vous pouvez commencer la fin de la chaîne.Vous devez utiliser à la
Range<String.Index>
place du classiqueNSRange
. La façon dont je le fais (peut-être qu'il y a une meilleure façon) est de prendre la corde et deString.Index
la déplaceradvance
.Je ne sais pas quelle plage vous essayez de remplacer, mais supposons que vous vouliez remplacer les 2 premiers caractères.
la source
Cette réponse de Martin R semble être correcte car elle représente Unicode.
Cependant au moment de la publication (Swift 1) son code ne se compile pas dans Swift 2.0 (Xcode 7), car ils ont supprimé
advance()
fonction. La version mise à jour est ci-dessous:Swift 2
Swift 3
Swift 4
la source
Ceci est similaire à la réponse d'Emilie cependant puisque vous avez demandé spécifiquement comment convertir le
NSRange
toRange<String.Index>
you ferait quelque chose comme ceci:la source
NSRange
parle, bien que ses composants soient des entiers) contre la vue des caractères de la chaîne, qui peut échouer lorsqu'elle est utilisée sur une chaîne avec des "caractères" qui nécessitent plus d'une unité UTF16 pour s'exprimer.Un riff sur la grande réponse de @Emilie, pas une réponse de remplacement / concurrente.
(Xcode6-Beta5)
Production:
Notez que les index représentent plusieurs unités de code. Le drapeau (REGIONAL INDICATOR SYMBOL LETTERS ES) est de 8 octets et le (VISAGE AVEC DES LARMES DE JOIE) est de 4 octets. (Dans ce cas particulier, il s'avère que le nombre d'octets est le même pour les représentations UTF-8, UTF-16 et UTF-32.)
Envelopper dans une fonction:
Production:
la source
la source
Dans Swift 2.0 en supposant
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
:la source
Voici mon meilleur effort. Mais cela ne peut pas vérifier ou détecter un argument d'entrée incorrect.
Résultat.
Détails
NSRange
à partir desNSString
décomptes UTF-16 code-unité s. EtRange<String.Index>
de SwiftString
est un opaque type relatif qui ne fournit que des opérations d'égalité et de navigation. C'est une conception intentionnellement cachée.Bien que cela
Range<String.Index>
semble être mappé sur le décalage d'unité de code UTF-16, ce n'est qu'un détail d'implémentation, et je n'ai trouvé aucune mention de garantie. Cela signifie que les détails de mise en œuvre peuvent être modifiés à tout moment. Représentation interne de SwiftString
n'est pas assez définie, et je ne peux pas m'y fier.NSRange
les valeurs peuvent être directement mappées auxString.UTF16View
index. Mais il n'y a pas de méthode pour le convertir enString.Index
.Swift
String.Index
est un index pour itérer SwiftCharacter
qui est un cluster de graphèmes Unicode . Ensuite, vous devez fournir une bonneNSRange
sélection des grappes de graphèmes correctes. Si vous fournissez une plage incorrecte comme dans l'exemple ci-dessus, cela produira un résultat incorrect car la plage de grappe de graphèmes appropriée n'a pas pu être déterminée.S'il y a une garantie que le
String.Index
est un code-unité UTF-16 offset, puis problème devient simple. Mais il est peu probable que cela se produise.Conversion inverse
Quoi qu'il en soit, la conversion inverse peut être effectuée avec précision.
Résultat.
la source
return NSRange(location: a.utf16Count, length: b.utf16Count)
doit être changé pourreturn NSRange(location: a.utf16.count, length: b.utf16.count)
J'ai trouvé que la solution swift2 la plus propre consiste à créer une catégorie sur NSRange:
Et puis appelez-le depuis pour la fonction de délégué de champ de texte:
la source
La documentation officielle de la version bêta de Swift 3.0 a fourni sa solution standard pour cette situation sous le titre String.UTF16View dans la section UTF16View Elements Match NSString Characters title
la source
Dans la réponse acceptée, je trouve les options lourdes. Cela fonctionne avec Swift 3 et ne semble avoir aucun problème avec les emojis.
la source
la source