¿Cómo funciona String.Index en Swift?
substring swift 4 (3)
Cree un UITextView dentro de un tableViewController. Usé la función: textViewDidChange y luego verifiqué la entrada de la tecla de retorno. luego, si detectó la entrada de la tecla de retorno, elimine la entrada de la tecla de retorno y descarte el teclado.
func textViewDidChange(_ textView: UITextView) {
tableView.beginUpdates()
if textView.text.contains("/n"){
textView.text.remove(at: textView.text.index(before: textView.text.endIndex))
textView.resignFirstResponder()
}
tableView.endUpdates()
}
He estado actualizando algunos de mis códigos y respuestas anteriores con Swift 3, pero cuando llegué a Swift Strings and Indexing fue muy difícil entender las cosas.
Específicamente estaba intentando lo siguiente:
let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error
donde la segunda línea me daba el siguiente error
''advancedBy'' no está disponible: para avanzar un índice en n pasos, llame a ''index (_: offsetBy :)'' en la instancia de CharacterView que produjo el índice.
Veo que
String
tiene los siguientes métodos.
str.index(after: String.Index)
str.index(before: String.Index)
str.index(String.Index, offsetBy: String.IndexDistance)
str.index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
Al principio me confundían mucho, así que comencé a jugar con ellos hasta que los entendí. Añado una respuesta a continuación para mostrar cómo se usan.
Solución completa Swift 4:
OffsetIndexableCollection (String usando Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
Todos los siguientes ejemplos usan
var str = "Hello, playground"
startIndex
y
endIndex
-
startIndex
es el índice del primer carácter -
endIndex
es el índice después del último carácter.
Ejemplo
// character
str[str.startIndex] // H
str[str.endIndex] // error: after last character
// range
let range = str.startIndex..<str.endIndex
str[range] // "Hello, playground"
Con los rangos unilaterales de Swift 4, el rango se puede simplificar a una de las siguientes formas.
let range = str.startIndex...
let range = ..<str.endIndex
Usaré el formulario completo en los siguientes ejemplos por razones de claridad, pero por razones de legibilidad, probablemente querrá usar los rangos unilaterales en su código.
after
Como en:
index(after: String.Index)
-
after
refiere al índice del personaje directamente después del índice dado.
Ejemplos
// character
let index = str.index(after: str.startIndex)
str[index] // "e"
// range
let range = str.index(after: str.startIndex)..<str.endIndex
str[range] // "ello, playground"
before
Como en:
index(before: String.Index)
-
before
refiere al índice del personaje directamente antes del índice dado.
Ejemplos
// character
let index = str.index(before: str.endIndex)
str[index] // d
// range
let range = str.startIndex..<str.index(before: str.endIndex)
str[range] // Hello, playgroun
offsetBy
Como en:
index(String.Index, offsetBy: String.IndexDistance)
-
El valor
offsetBy
puede ser positivo o negativo y comienza desde el índice dado. Aunque es del tipoString.IndexDistance
, puede darle unInt
.
Ejemplos
// character
let index = str.index(str.startIndex, offsetBy: 7)
str[index] // p
// range
let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end
str[range] // play
limitedBy
Como en:
index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)
-
limitedBy
es útil para asegurarse de que el desplazamiento no haga que el índice se salga de los límites. Es un índice delimitador. Como es posible que el desplazamiento supere el límite, este método devuelve un Opcional. Devuelvenil
si el índice está fuera de límites.
Ejemplo
// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
str[index] // p
}
Si el desplazamiento hubiera sido
77
lugar de
7
, entonces la declaración
if
se habría omitido.
¿Por qué se necesita String.Index?
Sería
mucho
más fácil usar un índice
Int
para cadenas.
La razón por la que debe crear una nueva
String.Index
para cada Cadena es que los Personajes en Swift no tienen la misma longitud debajo del capó.
Un solo personaje Swift puede estar compuesto por uno, dos o incluso más puntos de código Unicode.
Por lo tanto, cada cadena única debe calcular los índices de sus caracteres.
Posiblemente es ocultar esta complejidad detrás de una extensión de índice Int, pero soy reacio a hacerlo. Es bueno recordar lo que realmente está sucediendo.