web-dev-qa-db-fra.com

Formate le texte UITextField sans que le curseur ne soit déplacé à la fin

J'essaie d'appliquer NSAttributedString styles à un UITextField après le traitement d'une nouvelle entrée de texte, frappe par frappe. Le problème est que chaque fois que je remplace le texte, le curseur saute à la fin de chaque changement. Voici mon approche actuelle…

Recevoir et afficher le changement de texte immédiatement (le même problème se pose lorsque I renvoie false et effectue le remplacement du texte manuellement)

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {        
    let range:NSRange = NSRange(location: range.location, length: range.length)
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string);
    return true
}

Je suis abonné à la notification UITextFieldTextDidChangeNotification. Cela déclenche le style. Lorsque le texte est modifié, je le remplace ( NSString ) par la version formatée ( NSAttributedString ) à l’aide d’une fonction format ().

func textFieldTextDidChangeNotification(userInfo:NSDictionary) {
    var attributedString:NSMutableAttributedString = NSMutableAttributedString(string: fullString)
    attributedString = format(textField.text) as NSMutableAttributedString
    textField.attributedText = attributedString
}

Le style fonctionne bien comme ça. Cependant, après chaque remplacement de texte, le curseur passe à la fin de la chaîne. Comment puis-je désactiver ce comportement ou déplacer manuellement le curseur à l'endroit où il a commencé la modification? … Ou existe-t-il une meilleure solution pour styliser le texte lors de l'édition?

21
Bernd

Pour ce faire, saisissez l'emplacement du curseur, mettez à jour le contenu du champ, puis replacez le curseur à sa position d'origine. Je ne suis pas sûr de l'équivalent exact dans Swift, mais voici comment je procéderais dans Obj-C.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    UITextPosition *beginning = textField.beginningOfDocument;
    UITextPosition *cursorLocation = [textField positionFromPosition:beginning offset:(range.location + string.length)];

    textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];

    /* MAKE YOUR CHANGES TO THE FIELD CONTENTS AS NEEDED HERE */

    // cursorLocation will be (null) if you're inputting text at the end of the string
    // if already at the end, no need to change location as it will default to end anyway
    if(cursorLocation)
    {
        // set start/end location to same spot so that nothing is highlighted
        [textField setSelectedTextRange:[textField textRangeFromPosition:cursorLocation toPosition:cursorLocation]];
    }

    return NO;
}
36
Stonz2

Merci @ Stonz2 par le code en Objective-C. Il fonctionne comme un charme! Je l'ai utilisé dans mon projet Swift. Le même code dans Swift:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {

    let positionOriginal = textField.beginningOfDocument
    let cursorLocation = textField.positionFromPosition(positionOriginal, offset: (range.location + NSString(string: string).length))

    /* MAKE YOUR CHANGES TO THE FIELD CONTENTS AS NEEDED HERE */

    if(cursorLocation != nil) {
        textField.selectedTextRange = textField.textRangeFromPosition(cursorLocation!, toPosition: cursorLocation!)
    }
    return false 
}
9
pcsantana

C'est une vieille question, mais j'avais un problème similaire que j'ai résolu avec le Swift suivant:

// store the current cursor position as a range
var preAttributedRange: NSRange = textField.selectedRange 
// apply attributed string
var attributedString:NSMutableAttributedString = NSMutableAttributedString(string: fullString)
attributedString = format(textField.text) as NSMutableAttributedString
textField.attributedText = attributedString
// reapply the range
textField.selectedRange = preAttributedRange

Cela fonctionne dans le contexte de mon application, espérons que c'est utile pour quelqu'un!

9
jolyonruss

Ceci est un code fonctionnant pour Swift 2. Profitez-en! 

 func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let beginnig: UITextPosition =  textField.beginningOfDocument

    if let txt = textField.text {
        textField.text = NSString(string: txt).stringByReplacingCharactersInRange(range, withString: string)
    }

    if let cursorLocation: UITextPosition = textField.positionFromPosition(beginnig, offset: (range.location + string.characters.count) ) {
        textField.selectedTextRange = textField.textRangeFromPosition(cursorLocation, toPosition: cursorLocation)
    }

    return false
}

Notez que le dernier "if let" devrait toujours rester à la fin du code.

2
DuffMck

Pour compléter les autres réponses correctes de ce fil, voici quelques extraits de code pour Swift 4.2 et pour les deux UITextField et UITextView

UITextField

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // Update Cursor
    let positionOriginal = textField.beginningOfDocument
    let cursorLocation = textField.position(from: positionOriginal, offset: (range.location + string.count))
    if let cursorLocation = cursorLocation {
        textField.selectedTextRange = textField.textRange(from: cursorLocation, to: cursorLocation)
    }
    return false
}

UITextView

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    // Update Cursor
    let positionOriginal = textView.beginningOfDocument
    let cursorLocation = textView.position(from: positionOriginal, offset: (range.location + text.count))
    if let cursorLocation = cursorLocation {
        textView.selectedTextRange = textView.textRange(from: cursorLocation, to: cursorLocation)
    }
    return false
}
0
anders