web-dev-qa-db-fra.com

Comment String.Index fonctionne-t-il dans Swift?

J'ai mis à jour une partie de mon ancien code et des réponses avec Swift 3, mais lorsque je suis arrivé à Swift Strings et à l'indexation, il m'a été difficile de comprendre les choses. 

Plus précisément, j'essayais ce qui suit:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5) // error

où la deuxième ligne me donnait l'erreur suivante

'advancedBy' n'est pas disponible: pour faire avancer un index de n étapes, appelez 'index (_: offsetBy :)' sur l'occurrence CharacterView qui a généré l'index.

Je vois que String a les méthodes suivantes.

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)

C’était vraiment déroutant au début, alors j’ai commencé à jouer avec eux jusqu’à ce que je les comprenne. J'ajoute une réponse ci-dessous pour montrer comment ils sont utilisés.

74
Suragch

 enter image description here

Tous les exemples suivants utilisent 

var str = "Hello, playground"

startIndex et endIndex

  • startIndex est l'index du premier caractère
  • endIndex est l'index after le dernier caractère.

Exemple

// character
str[str.startIndex] // H
str[str.endIndex]   // error: after last character

// range
let range = str.startIndex..<str.endIndex
str[range]  // "Hello, playground"

Avec Swift 4's gammes unilatérales , la plage peut être simplifiée à l’une des formes suivantes.

let range = str.startIndex...
let range = ..<str.endIndex

Je vais utiliser le formulaire complet dans les exemples suivants pour des raisons de clarté, mais par souci de lisibilité, vous voudrez probablement utiliser les plages unilatérales de votre code.

after

Comme dans: index(after: String.Index)

  • after fait référence à l'index du caractère directement après l'index donné.

Exemples

// 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

Comme dans: index(before: String.Index)

  • before fait référence à l'index du caractère directement avant l'index donné.

Exemples

// 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

Comme dans: index(String.Index, offsetBy: String.IndexDistance)

  • La valeur offsetBy peut être positive ou négative et commence à partir de l'index donné. Bien qu'il soit du type String.IndexDistance, vous pouvez lui donner une Int.

Exemples

// 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

Comme dans: index(String.Index, offsetBy: String.IndexDistance, limitedBy: String.Index)

  • La limitedBy est utile pour s'assurer que le décalage ne provoque pas l'index pour sortir des limites. C'est un index englobant. Comme il est possible que le décalage dépasse la limite, cette méthode retourne une option. Il retourne nil si l'index est hors limites.

Exemple

// character
if let index = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
    str[index]  // p
}

Si l'offset avait été 77 au lieu de 7, l'instruction if aurait été ignorée.

Pourquoi String.Index est-il nécessaire?

Il serait beaucoup plus facile d'utiliser un index Int pour Strings. La raison pour laquelle vous devez créer un nouveau String.Index pour chaque chaîne est que les caractères dans Swift ne sont pas tous de la même longueur sous le capot. Un seul caractère Swift peut être composé d'un, de deux ou même de plusieurs points de code Unicode. Ainsi, chaque chaîne unique doit calculer les index de ses caractères.

C'est peut-être pour cacher cette complexité derrière une extension d'index Int, mais je suis réticent à le faire. Il est bon de se rappeler de ce qui se passe réellement.

204
Suragch

Je faisais face au même problème et je me suis retrouvé avec une solution adaptée à mes besoins. J'ai un UITextView à l'intérieur d'un tableViewController. J'ai utilisé function: textViewDidChange, puis vérifié pour return-key-input . Puis s'il détectait return-key-input, j'ai supprimé l'entrée de la touche de retour et ignoré le clavier.

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()
}
0
Karl Mogensen