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?
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;
}
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
}
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!
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.
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
}