Obtenir et définir la position du curseur de UITextField et UITextView dans Swift

109

J'ai expérimenté UITextFieldet comment travailler avec la position du curseur. J'ai trouvé un certain nombre de réponses Objective-C relation, comme dans

Mais comme je travaille avec Swift, je voulais apprendre comment obtenir l'emplacement actuel du curseur et le définir également dans Swift.

La réponse ci-dessous est le résultat de mon expérimentation et de ma traduction d'Objective-C.

Suragch
la source

Réponses:

316

Le contenu suivant s'applique à la fois UITextFieldet UITextView.

Informations utiles

Le tout début du texte du champ de texte:

let startPosition: UITextPosition = textField.beginningOfDocument

La toute fin du texte du champ de texte:

let endPosition: UITextPosition = textField.endOfDocument

La plage actuellement sélectionnée:

let selectedRange: UITextRange? = textField.selectedTextRange

Obtenir la position du curseur

if let selectedRange = textField.selectedTextRange {

    let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)

    print("\(cursorPosition)")
}

Définir la position du curseur

Afin de définir la position, toutes ces méthodes définissent en fait une plage avec les mêmes valeurs de début et de fin.

Au début

let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)

Jusqu'à la fin

let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)

Vers une position à gauche de la position actuelle du curseur

// only if there is a currently selected range
if let selectedRange = textField.selectedTextRange {

    // and only if the new position is valid
    if let newPosition = textField.position(from: selectedRange.start, offset: -1) {

        // set the new position
        textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
    }
}

Vers une position arbitraire

Commencez par le début et déplacez 5 caractères vers la droite.

let arbitraryValue: Int = 5
if let newPosition = textField.position(from: textField.beginningOfDocument, offset: arbitraryValue) {

    textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}

en relation

Sélectionnez tout le texte

textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)

Sélectionnez une plage de texte

// Range: 3 to 7
let startPosition = textField.position(from: textField.beginningOfDocument, offset: 3)
let endPosition = textField.position(from: textField.beginningOfDocument, offset: 7)

if startPosition != nil && endPosition != nil {
    textField.selectedTextRange = textField.textRange(from: startPosition!, to: endPosition!)
}

Insérer du texte à la position actuelle du curseur

textField.insertText("Hello")

Remarques

  • Utilisez textField.becomeFirstResponder()pour donner le focus au champ de texte et faire apparaître le clavier.

  • Voir cette réponse pour savoir comment obtenir le texte à une certaine distance.

Voir également

Suragch
la source
Quel est le but de startPosition? Que pouvez-vous en faire? Quel est également le but dont on pourrait avoir besoin pour obtenir la position du curseur?
Honey
2
@Honey, j'ai utilisé ce nom de variable pour faire référence à deux choses différentes ici. La première était de faire référence au début du champ de texte. Ce serait utile si vous vouliez y déplacer le curseur. La seconde était de faire référence à la mendicité d'une plage de texte sélectionnée. C'est utile si vous souhaitez copier cette plage ou y définir un attribut.
Suragch le
Y a-t-il quelque chose pour déplacer le curseur vers la droite alors que le champ de texte est vide? J'ai suivi votre réponse mais je n'ai pas pu y arriver.
Yucel Bayram
1
@yucelbayram, Vous pouvez placer le curseur après le H en utilisant textField.endOfDocument. Vous pouvez également utiliser des traits de soulignement ou des espaces pour représenter les lettres manquantes.
Suragch
1
@ArielSD, je suis désolé, je n'ai pas travaillé dessus depuis un moment. Je ne m'en souviens plus.
Suragch du
42

dans mon cas, j'ai dû utiliser DispatchQueue:

func textViewDidBeginEditing(_ textView: UITextView) {

   DispatchQueue.main.async{
      textField.selectedTextRange = ...
   }
}

rien d'autre de cela et d'autres fils n'a fonctionné.

PS: J'ai vérifié à nouveau sur quel thread textViewDidBeginEditing s'exécutait, et c'était le thread principal, car toute l'interface utilisateur devrait fonctionner, donc je ne sais pas pourquoi ce petit retard d'utilisation de main.asynch a fonctionné.

Michael Ros
la source
2
Ce n'est pas un retard mais la prochaine boucle d'exécution. La plage sélectionnée du champ de texte est très probablement modifiée par la même fonction que celle appelée textViewDidBeginEditing. Donc, en le plaçant dans la file d'attente, cela se produira une fois que l'appelant aura terminé.
Michael Ozeryansky
@MichaelOzeryansky Ouais, je le pense aussi. Mais cela soulève la question - quelle est la bonne méthode pour définir manuellement la position du curseur?
timeowonder
a également fonctionné cette solution dans mon cas
Hattori Hanzō
1

Pour définir la position du curseur à votre point:

textView.beginFloatingCursor(at: CGPoint(x: 10.0, y: 10.0))

Pour réinitialiser la position du curseur:

textView.endFloatingCursor()

Remarque : cet exemple fonctionne à la fois dans Textview et Textfield.

Parth Patel
la source
@Suragch Floating Cursor est une méthode pour définir l'emplacement du curseur dans textView ou textField.
Parth Patel
1
let range = field.selectedTextRange

field.text = "hello world"

field.selectedTextRange = range 
Ivan
la source