¿Cómo puedo convertir NSRangea Range<String.Index>en Swift?
Quiero usar el siguiente UITextFieldDelegatemétodo:
func textField(textField: UITextField!,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String!) -> Bool {
textField.text.stringByReplacingCharactersInRange(???, withString: string)


Respuestas:
La
NSStringversión (a diferencia de Swift String) dereplacingCharacters(in: NSRange, with: NSString)acepta unNSRange, por lo que una solución simple es convertirStringaNSStringprimero . Los nombres de los métodos de delegado y reemplazo son ligeramente diferentes en Swift 3 y 2, por lo que, dependiendo de qué Swift esté usando:Swift 3.0
Swift 2.x
fuente
textFieldcontiene caracteres unicode de unidad de código múltiple como emoji. Dado que es un campo de entrada, un usuario puede ingresar emoji (😂), otros caracteres de la página 1 de caracteres de unidades de múltiples códigos, como banderas (🇪🇸).UITextFieldDelegateeltextField:shouldChangeCharactersInRange:replacementString:método no proporcionará un rango que comience o termine dentro de un carácter unicode, ya que la devolución de llamada se basa en que un usuario ingresa caracteres desde el teclado, y no es posible escribir solo una parte de un personaje emoji unicode. Tenga en cuenta que si el delegado se escribiera en Obj-C, tendría el mismo problema.(textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)A partir de Swift 4 (Xcode 9), la biblioteca estándar de Swift proporciona métodos para convertir entre rangos de cadenas Swift (
Range<String.Index>) yNSStringrangos (NSRange). Ejemplo:Por lo tanto, el reemplazo de texto en el método de delegado de campo de texto ahora se puede hacer como
(Respuestas anteriores para Swift 3 y anteriores :)
A partir de Swift 1.2,
String.Indextiene un inicializadorque se puede utilizar para convertir
NSRangeaRange<String.Index>correctamente (incluyendo todos los casos de Emojis, Indicadores Regionales u otras agrupaciones de grafema prolongados) sin conversión intermedia a unaNSString:Este método devuelve un rango de cadena opcional porque no todos los
NSRanges son válidos para una cadena Swift determinada.El
UITextFieldDelegatemétodo delegado se puede escribir comoLa conversión inversa es
Una prueba simple:
Actualización para Swift 2:
La versión de Swift 2
rangeFromNSRange()ya fue dada por Serhii Yakovenko en esta respuesta , la incluyo aquí para completar:La versión Swift 2 de
NSRangeFromRange()esActualización para Swift 3 (Xcode 8):
Ejemplo:
fuente
rangees(0,2)porque seNSRangerefiere a los caracteres UTF-16 en unNSString. Pero cuenta como un personaje Unicode en una cadena Swift. -let end = advance(start, range.length)de la respuesta referenciada se bloquea en este caso con el mensaje de error "error fatal: no se puede incrementar endIndex".Stringpara saltar a la referencia de la API, leer todos los métodos y comentarios, luego probar diferentes cosas hasta que funcione :)let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex), creo que realmente quieres limitarloutf16.endIndex - 1. De lo contrario, puede comenzar desde el final de la cadena.Necesita usar en
Range<String.Index>lugar del clásicoNSRange. La forma en que lo hago (tal vez hay una mejor manera) es tomando la cuerdaString.Indexy moviéndolaadvance.No sé qué rango está tratando de reemplazar, pero supongamos que desea reemplazar los primeros 2 caracteres.
fuente
Esta respuesta de Martin R parece ser correcta porque representa Unicode.
Sin embargo, en el momento de la publicación (Swift 1) su código no se compila en Swift 2.0 (Xcode 7), porque eliminaron la
advance()función. La versión actualizada está abajo:Swift 2
Swift 3
Swift 4
fuente
Sin embargo, esto es similar a la respuesta de Emilie, ya que usted preguntó específicamente cómo convertirlo
NSRangeaRange<String.Index>usted haría algo como esto:fuente
NSRangehabla, aunque sus componentes son enteros) contra la vista de caracteres de la cadena, que puede fallar cuando se usa en una cadena con "caracteres" que requieren más de una unidad UTF16 para expresarse.Un riff sobre la gran respuesta de @Emilie, no una respuesta de reemplazo / competencia.
(Xcode6-Beta5)
Salida:
Observe que los índices representan múltiples unidades de código. La bandera (LETRAS DE SÍMBOLO DEL INDICADOR REGIONAL ES) tiene 8 bytes y la (CARA CON LÁGRIMAS DE ALEGRÍA) tiene 4 bytes. (En este caso particular, resulta que el número de bytes es el mismo para las representaciones UTF-8, UTF-16 y UTF-32).
Envolviéndolo en una función:
Salida:
fuente
fuente
En Swift 2.0 asumiendo
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {:fuente
Aquí está mi mejor esfuerzo. Pero esto no puede verificar o detectar un argumento de entrada incorrecto.
Resultado.
Detalles
NSRangede losNSStringrecuentos de unidades de código UTF-16 . YRange<String.Index>de SwiftStringes un opaco tipo relativo que proporciona solo operaciones de igualdad y navegación. Este es un diseño intencionalmente oculto.Aunque
Range<String.Index>parece estar asignado al desplazamiento de la unidad de código UTF-16, eso es solo un detalle de implementación, y no pude encontrar ninguna mención sobre ninguna garantía. Eso significa que los detalles de implementación se pueden cambiar en cualquier momento. La representación interna de SwiftStringno está bastante definida, y no puedo confiar en ella.NSRangelos valores se pueden asignar directamente a losString.UTF16Viewíndices. Pero no hay ningún método para convertirlo enString.Index.Swift
String.Indexes un índice para iterar Swift,Characterque es un clúster de grafema Unicode . Luego, debe proporcionar el adecuadoNSRangeque selecciona los grupos de grafemas correctos. Si proporciona un rango incorrecto como en el ejemplo anterior, producirá un resultado incorrecto porque no se pudo determinar el rango adecuado del grupo de grafemas.Si hay una garantía de que el
String.Indexes UTF-16-unidad de desplazamiento de código, a continuación, se convierte en un problema sencillo. Pero es poco probable que suceda.Conversión inversa
De todos modos, la conversión inversa se puede hacer con precisión.
Resultado.
fuente
return NSRange(location: a.utf16Count, length: b.utf16Count)debe cambiarse areturn NSRange(location: a.utf16.count, length: b.utf16.count)He encontrado que la única solución más limpia de swift2 es crear una categoría en NSRange:
Y luego llame desde la función de delegado de campo de texto:
fuente
La documentación oficial de Swift 3.0 beta ha proporcionado su solución estándar para esta situación bajo el título String.UTF16View en la sección UTF16View Elements Match NSString Characters title
fuente
En la respuesta aceptada encuentro los opcionales engorrosos. Esto funciona con Swift 3 y parece no tener problemas con los emojis.
fuente
fuente