web-dev-qa-db-fra.com

Détecter le retour arrière dans UITextField vide

Y at-il un moyen de détecter quand le Backspace/Delete La touche est enfoncée dans le clavier de l'iPhone sur un UITextField qui est vide? Je veux savoir quand Backspace n'est enfoncé que si le UITextField est vide.


D'après la suggestion de @Alex Reynolds dans un commentaire, j'ai ajouté le code suivant lors de la création de mon champ de texte:

[[NSNotificationCenter defaultCenter] addObserver:self
          selector:@selector(handleTextFieldChanged:)
              name:UITextFieldTextDidChangeNotification
            object:searchTextField];

Cette notification est reçue (la fonction handleTextFieldChanged est appelée), mais pas quand je presse le Backspace clé dans un champ vide. Des idées?


Il semble y avoir une certaine confusion autour de cette question. Je souhaite recevoir une notification lorsque le Backspace la touche est enfoncée. C'est ça. Mais la solution doit également fonctionner lorsque le UITextField est déjà vide.

120
marcc

Cela peut être long mais cela pourrait marcher. Essayez de définir le texte du champ de texte sur un caractère de largeur nulle \u200B. Lorsque vous appuyez sur la touche Retour arrière sur un champ de texte qui semble vide, votre espace sera supprimé. Ensuite, vous pouvez simplement réinsérer l'espace.

Peut ne pas fonctionner si l'utilisateur parvient à déplacer le curseur à gauche de l'espace.

34
Andrew

Swift 4:


Sous-classe UITextField:

// MyTextField.Swift
import UIKit

protocol MyTextFieldDelegate {
    func textFieldDidDelete()
}

class MyTextField: UITextField {

    var myDelegate: MyTextFieldDelegate?

    override func deleteBackward() {
        super.deleteBackward()
        myDelegate?.textFieldDidDelete()
    }

}

La mise en oeuvre:

// ViewController.Swift

import UIKit

class ViewController: UIViewController, MyTextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        // initialize textField
        let input = MyTextField(frame: CGRect(x: 50, y: 50, width: 150, height: 40))

        // set viewController as "myDelegate"
        input.myDelegate = self

        // add textField to view
        view.addSubview(input)

        // focus the text field
        input.becomeFirstResponder()
    }

    func textFieldDidDelete() {
        print("delete")
    }

}

Objectif c:


Sous-classe UITextField:

//Header
//MyTextField.h

//create delegate protocol
@protocol MyTextFieldDelegate <NSObject>
@optional
- (void)textFieldDidDelete;
@end

@interface MyTextField : UITextField<UIKeyInput>

//create "myDelegate"
@property (nonatomic, assign) id<MyTextFieldDelegate> myDelegate;
@end

//Implementation
#import "MyTextField.h"

@implementation MyTextField

- (void)deleteBackward {
    [super deleteBackward];

    if ([_myDelegate respondsToSelector:@selector(textFieldDidDelete)]){
        [_myDelegate textFieldDidDelete];
    }
}

@end

Ajoutez maintenant simplement MyTextFieldDelegate à votre UIViewController et définissez votre UITextFieldsmyDelegate à self:

//View Controller Header
#import "MyTextField.h"

//add "MyTextFieldDelegate" to you view controller
@interface ViewController : UIViewController <MyTextFieldDelegate>
@end

//View Controller Implementation
- (void)viewDidLoad {
    //initialize your text field
    MyTextField *input = 
     [[MyTextField alloc] initWithFrame:CGRectMake(0, 0, 70, 30)];

    //set your view controller as "myDelegate"
    input.myDelegate = self;

    //add your text field to the view
    [self.view addSubview:input];
}

//MyTextField Delegate
- (void)textFieldDidDelete {
    NSLog(@"delete");
}
140
Jacob Caraballo

Mise à jour: voir réponse de JacobCaraballo pour un exemple qui remplace -[UITextField deleteBackward].

Vérifier UITextInput , plus précisément UIKeyInput a une deleteBackward méthode déléguée que toujours est appelé lorsque vous appuyez sur la touche Suppr. Si vous faites quelque chose de simple, vous pouvez alors envisager de simplement sous-classer UILabel et de le rendre conforme au protocole UIKeyInput, comme cela est fait par SimpleTextInput et cela Exemple pour iPhone UIKeyInputRemarque: UITextInput et ses parents (y compris UIKeyInput) ne sont disponibles que dans iOS 3.2 et versions ultérieures.

40
ma11hew28

Code comme suit:

@interface MyTextField : UITextField
@end

@implementation MyTextField

- (void)deleteBackward
{
    [super deleteBackward];

    //At here, you can handle backspace key pressed event even the text field is empty
}

@end

Enfin, oubliez de modifier la propriété Custom Class du champ de texte en "MyTextField"

31
Jeffrey Loo

Mise en œuvre rapide:

import UIKit

protocol PinTexFieldDelegate : UITextFieldDelegate {
    func didPressBackspace(textField : PinTextField)
}

class PinTextField: UITextField {

    override func deleteBackward() {
        super.deleteBackward()

        // If conforming to our extension protocol
        if let pinDelegate = self.delegate as? PinTexFieldDelegate {
            pinDelegate.didPressBackspace(self)
        }
    }
}
18
Ruud Visser

J'ai trouvé un moyen plus simple que la solution subclass. Même son petit peu étrange mais ça fonctionne bien.

- (BOOL)textView:(UITextView *)textView 
        shouldChangeTextInRange:(NSRange)range 
        replacementText:(NSString *)text
{
    const char * _char = [text cStringUsingEncoding:NSUTF8StringEncoding];
    int isBackSpace = strcmp(_char, "\b");

    if (isBackSpace == -8) {
       // is backspace
    }

    return YES;
}

C'est un peu étrange pour le résultat de comparer est -8. Peut-être que je me trompe à un moment donné de C Programming. Mais son bon travail;)

17
Jerapong Nampetch

Essayez le delegate

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string {

Puis vérifiez si le range.length == 1 _ qui semble être le cas lorsque backspace est touché.

10
Niklas Alvaeus

veuillez utiliser le code ci-dessous, il vous aidera à détecter la touche de suppression du clavier, même si votre champ de texte est vide.

Objectif C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { return YES; }

Swift:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { return true }
10
Pritesh

La réponse de Niklas Alvaeus m'a aidé avec un problème similaire

Je limitais l'entrée à un jeu de caractères spécifique, mais il ignorait les espaces de retour. Alors je l'ai fait vérifier range.length == 1 avant de couper le NSString. Si c'est vrai, je retourne juste la chaîne et ne la coupe pas. Voir ci-dessous

 - (BOOL) textField:(UITextField *)textField 
          shouldChangeCharactersInRange:(NSRange)range 
          replacementString:(NSString *)string
 {
     NSCharacterSet *nonNumberSet = 
      [[NSCharacterSet characterSetWithCharactersInString:@"0123456789."] 
         invertedSet];

    if (range.length == 1) {
       return string;
    }
    else {
       return ([string stringByTrimmingCharactersInSet:nonNumberSet].length > 0);
    }   
 }
8
Ben Call

Oui, utilisez la méthode ci-dessous pour détecter le retour arrière lorsque textField est vide.

Besoin d'ajouter ITextFieldDelegate

yourTextField.delegate = self (DOIT OBLIGER)

Swift:

func keyboardInputShouldDelete(_ textField: UITextField) -> Bool { 
    return true
}

Objectif C:

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField { 
    return YES; 
}
5
Himanshu padia

Pour ceux qui ont des problèmes avec la réponse de Jacob , j'ai implémenté ma sous-classe textfield comme suit et cela fonctionne très bien!

#import <UIKit/UIKit.h>

@class HTTextField;

@protocol HTBackspaceDelegate <NSObject>

@optional
- (void)textFieldDidBackspace:(HTTextField*)textField;
@end

@interface HTTextField : UITextField<UIKeyInput>

@property (nonatomic, assign) id<HTBackspaceDelegate> backspaceDelegate;

@end


#import "HTTextField.h"

@implementation HTTextField

- (void)deleteBackward {
    [super deleteBackward];
    if ([self.backspaceDelegate respondsToSelector:@selector(textFieldDidBackspace:)]){
        [self.backspaceDelegate textFieldDidBackspace:self];
    }
}

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {
    BOOL shouldDelete = YES;

    if ([UITextField instancesRespondToSelector:_cmd]) {
        BOOL (*keyboardInputShouldDelete)(id, SEL, UITextField *) = (BOOL (*)(id, SEL, UITextField *))[UITextField instanceMethodForSelector:_cmd];

        if (keyboardInputShouldDelete) {
            shouldDelete = keyboardInputShouldDelete(self, _cmd, textField);
        }
    }

    if (![textField.text length] && [[[UIDevice currentDevice] systemVersion] intValue] >= 8) {
        [self deleteBackward];
    }

    return shouldDelete;
}

@end
4
furkan3ayraktar

La meilleure utilisation que j'ai trouvée pour détecter le retour arrière est la détection lorsque l'utilisateur a appuyé sur le retour arrière dans un UITextField vide. Par exemple, si vous avez des destinataires "bouillonnés" dans l'application de messagerie, lorsque vous appuyez sur la touche retour arrière dans le UITextField, le dernier destinataire "bouillonné" est sélectionné.

Cela peut être fait de manière similaire à la réponse de Jacob Caraballo. Mais dans la réponse de Jacob, si le UITextField a encore un caractère lorsque vous appuyez sur la touche Retour arrière, au moment où le message de délégué est reçu, le UITextField sera déjà vide, vous détecterez donc le backspace sur un champ de texte comportant au plus un caractère.

En fait, si vous voulez détecter backspace sur un UITextField avec exactement zéro caractère (vide), vous devez alors envoyer le message à delegate avant l'appel à super deleteBackward. Par exemple:

#import "MyTextField.h"

//Text field that detects when backspace is hit with empty text
@implementation MyTextField

#pragma mark - UIKeyInput protocol
-(void)deleteBackward
{
  BOOL isTextFieldEmpty = (self.text.length == 0);
  if (isTextFieldEmpty) {
    if ([self.delegate 
         respondsToSelector:@selector(textFieldDidHitBackspaceWithEmptyText:)]) {

        [self.delegate textFieldDidHitBackspaceWithEmptyText:self];
        }
    }
    [super deleteBackward];
}
@end

L'interface pour un tel champ de texte ressemblerait à ceci:

@protocol MyTextFieldDelegate;

@interface MyTextField : UITextField
@property(nonatomic, weak) id<MyTextFieldDelegate> delegate;
@end

@protocol MyTextFieldDelegate <UITextFieldDelegate>
@optional
-(void)textFieldDidHitBackspaceWithEmptyText:(MyTextField *)textField;
@end
3
Sam

Dans iOS 6, la méthode deleteBackward est appelée sur UITextField lorsque l'utilisateur appuie sur la touche Retour arrière, y compris lorsque le champ est vide. Ainsi, vous pouvez sous-classer UITextField et fournir votre propre implémentation deleteBackward (en appelant également les super.)

Je supporte toujours iOS 5, alors il me faut une combinaison de la réponse d'Andrew et de celle-ci.

2
tracy

:) juste pour le titre "Detect backspace", où j'utilise UIKeyboardTypeNumberPad.

Je rencontre aussi la même question ce soir, et voici mon code pour le découvrir:

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string
{
    NSLog([NSString stringWithFormat:@"%d", [string length]]);
}

Parce qu'avec UIKeyboardTypeNumberPad, l'utilisateur ne peut saisir que le numéro ou le retour arrière. Ainsi, lorsque la longueur de la chaîne est 0, il doit s'agir d'une touche de retour arrière.

J'espère que ce qui précède aidera.

1
Jason Lee

Plutôt que d'essayer de préconstruire ce qui SERA dans le champ de texte ou de déterminer quel caractère spécial a été entré dans la méthode shouldChangeCharactersInRange, je suggère de procéder comme suit:

[self performSelector:@selector(manageSearchResultsDisplay) 
           withObject:nil 
           afterDelay:0];

Cela vous permet d'appeler une méthode directement après la fin de l'opération en cours. Ce qui est bien à ce sujet, c’est que, à la fin de celle-ci, la valeur modifiée sera déjà dans le UITextField. À ce stade, vous pouvez simplement vérifier sa longueur et/ou valider en fonction de ce qui est disponible.

1
Aaron

Dans le fichier .h, ajoutez le délégué UIKeyInput

- (BOOL)keyboardInputShouldDelete:(UITextField *)textField {

if ([textField isEqual:_txtFirstDigit]) {

}else if([textField isEqual:_txtSecondDigit]) {
    [_txtFirstDigit becomeFirstResponder];

}else if([textField isEqual:_txtThirdDigit]) {
    [_txtSecondDigit becomeFirstResponder];

}else if([textField isEqual:_txtFourthDigit]) {
    [_txtThirdDigit becomeFirstResponder];
}
return YES;
}   

amélioration du formatage

1
Anjali Prasad

La réponse la plus populaire Il manque une chose - la capacité de détecter si le champ de texte était vide ou non.

En d’autres termes, lorsque vous substituez la méthode deleteBackwards () d’une sous-classe TextField, vous ne savez toujours pas si le champ de texte était déjà vide. (Avant et après deleteBackwards (), textField.text! est une chaîne vide: "")

Voici mon amélioration, avec un contrôle de vide avant la suppression.

1. Créez un protocole de délégué qui étend UITextFieldDelegate

protocol MyTextFieldDelegate: UITextFieldDelegate {
    func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool)
}

2. Sous-classe UITextField

class MyTextField: UITextField {
    override func deleteBackward() {
        // see if text was empty
        let wasEmpty = text == nil || text! == ""

        // then perform normal behavior
        super.deleteBackward()

        // now, notify delegate (if existent)
        (delegate as? MyTextFieldDelegate)?.textField(self, didDeleteBackwardAnd: wasEmpty)
    }
}

3. Implémentez votre nouveau protocole de délégué

extension MyViewController: MyTextFieldDelegate {
    func textField(_ textField: UITextField, didDeleteBackwardAnd wasEmpty: Bool) {
        if wasEmpty {
            // do what you want here...
        }
    }
}
1
Tim

La sous-classe UITextField ne fonctionnait pas pour moi sur iOS 8.3, deleteBackward n'a jamais été appelé.

Voici la solution que j'ai utilisée, fonctionne sur toutes les versions d'iOS 8 et devrait également fonctionner sur d'autres versions d'iOS.

for textField in textFields {
            textField.text = " "
}

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        if string == "" && textField.text == " "   {
            // Do stuff here
            return false
        }
        return true
}
1
joel.d

J'ai implémenté la solution similaire avec des améliorations mineures qui me diront que si le champ de texte a une valeur alors que l'utilisateur a tapé sur le retour arrière. Ceci est utile dans mon cas lorsque je ne devrais me concentrer que sur un autre champ de texte si celui-ci est vide lorsque vous appuyez sur la touche Retour arrière.

protocol MyTextFieldDelegate : UITextFieldDelegate {
    func textFieldDidDelete(textField: MyTextField, hasValue: Bool)
}

override func deleteBackward() {
    let currentText = self.text ?? ""
    super.deleteBackward()
    let hasValue = currentText.isEmpty ? false : true
    if let delegate = self.delegate as? MyTextFieldDelegate {
        delegate.textFieldDidDelete(textField: self, hasValue: hasValue)
    }
}
1
Hemang