web-dev-qa-db-fra.com

Comment écarter le clavier pour UITextView avec la touche de retour?

Dans la bibliothèque d’IB, l’introduction nous dit que lorsque le return la touche est enfoncée, le clavier pour UITextView disparaîtra. Mais en réalité le return la clé ne peut agir que comme '\ n'.

Je peux ajouter un bouton et utiliser [txtView resignFirstResponder] pour masquer le clavier.

Mais y a-t-il moyen d'ajouter l'action pour le return touche dans le clavier afin que je n'ai pas besoin d'ajouter UIButton?

374
Chilly Zhong

UITextView n'a aucune méthode qui sera appelée lorsque l'utilisateur appuiera sur la touche de retour. Si vous voulez que l'utilisateur puisse ajouter une seule ligne de texte, utilisez un UITextField. Frapper le retour et cacher le clavier pour un UITextView ne suit pas les instructions de l'interface.

Même dans ce cas, si vous voulez faire cela, implémentez la méthode textView:shouldChangeTextInRange:replacementText: de UITextViewDelegate et dans cette vérification, si le texte de remplacement est \n, masquez le clavier.

Il y a peut-être d'autres moyens mais je n'en connais aucun.

343
lostInTransit

Je me suis dit que je publierais l'extrait ici:

Assurez-vous de déclarer la prise en charge du protocole UITextViewDelegate.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

    if([text isEqualToString:@"\n"]) {
        [textView resignFirstResponder];
        return NO;
    }

    return YES;
}

Mise à jour de Swift 4.0:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
        return false
    }
    return true
}
486
samvermette

Je sais que cela a déjà été répondu, mais je n'aime pas trop utiliser le littéral chaîne pour la nouvelle ligne, donc voici ce que j'ai fait.

- (BOOL)textView:(UITextView *)txtView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if( [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound ) {
        return YES;
    }

    [txtView resignFirstResponder];
    return NO;
}

Mise à jour de Swift 4.0:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text as NSString).rangeOfCharacter(from: CharacterSet.newlines).location == NSNotFound {
    return true
}
txtView.resignFirstResponder()
return false
}
74
ribeto

Je sais que l’on a souvent répondu à cette question, mais voici mes deux points de vue.

J'ai trouvé les réponses par samvermette et ribeto vraiment utiles, ainsi que par le commentaire par maxpower dans les ribeto répondre. Mais il y a un problème avec ces approches. Le problème que matt mentionne dans la réponse de samvermette est que si l'utilisateur veut coller quelque chose avec un saut de ligne à l'intérieur, le clavier se cacherait sans rien coller.

Donc, mon approche est un mélange des trois solutions mentionnées ci-dessus et vérifie seulement si la chaîne entrée est une nouvelle ligne lorsque la longueur de la chaîne est égale à 1, afin de nous assurer que l'utilisateur tape au lieu de coller.

Voici ce que j'ai fait:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSRange resultRange = [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSBackwardsSearch];
    if ([text length] == 1 && resultRange.location != NSNotFound) {
        [textView resignFirstResponder];
        return NO;
    }

    return YES;
}
42
josebama

Une méthode plus élégante consiste à fermer le clavier lorsque l'utilisateur appuie quelque part en dehors du cadre du clavier.

Tout d'abord, définissez l'affichage de votre ViewController sur la classe "UIControl" dans l'inspecteur d'identité dans UIBuilder. Faites glisser la vue dans le fichier d’en-tête de ViewController et reliez-la sous forme d’action à l’événement Touch Up Inside, par exemple:

ViewController.h

-(IBAction)dismissKeyboardOnTap:(id)sender;

Dans le fichier ViewController principal, ViewController.m:

-(IBAction)dismissKeyboardOnTap:(id)sender
    {
         [[self view] endEditing:YES];
    }

Vous pouvez exiger un double tap ou un toucher long en utilisant des techniques similaires. Vous devrez peut-être définir votre ViewController comme étant un UITextViewDelegate et connecter le TextView au ViewController. Cette méthode fonctionne à la fois pour UITextView et UITextField.

Source: Big Nerd Ranch

EDIT: J'aimerais également ajouter que si vous utilisez UIScrollView, la technique ci-dessus risque de ne pas fonctionner aussi facilement avec Interface Builder. Dans ce cas, vous pouvez utiliser un UIGestureRecognizer et appeler la méthode [[self view] endEditing: YES] à la place. Un exemple serait:

-(void)ViewDidLoad{
    ....
    UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc] 
        initWithTarget:self action:@selector(tap:)];
    [self.view addGestureRecognizer: tapRec];
    ....
}

-(void)tap:(UITapGestureRecognizer *)tapRec{
    [[self view] endEditing: YES];
}

Lorsque l'utilisateur appuie en dehors du clavier et ne touche pas un espace de saisie, le clavier est désactivé.

35
TMilligan

Ajoutez cette méthode dans votre contrôleur de vue.

Swift:

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
        return false
    }
    return true
}

Cette méthode peut aussi vous être utile:

/**
Dismiss keyboard when tapped outside the keyboard or textView

:param: touches the touches
:param: event   the related event
*/
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    if let touch = touches.anyObject() as? UITouch {
        if touch.phase == UITouchPhase.Began {
            textField?.resignFirstResponder()
        }
    }
}
32
Alexander Volkov
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    if([text isEqualToString:@"\n"])
        [textView resignFirstResponder];
    return YES;
}

yourtextView.delegate=self;

Ajoutez également UITextViewDelegate

N'oubliez pas de confirmer le protocole

SI vous n'avez pas ajouté if([text isEqualToString:@"\n"]) vous ne pouvez pas éditer

26
Vineesh TP

Il existe une autre solution lors de l’utilisation de uitextview: vous pouvez ajouter une barre d’outils en tant que InputAccessoryView dans "textViewShouldBeginEditing", et à partir du bouton Terminé de cette barre d’outils, vous pouvez ignorer le clavier. Le code correspondant est le suivant:

Dans viewDidLoad

toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)]; //toolbar is uitoolbar object
toolBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(btnClickedDone:)];
[toolBar setItems:[NSArray arrayWithObject:btnDone]];

Dans la méthode textviewdelegate

- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
     [textView setInputAccessoryView:toolBar];
     return YES;
}

En action de Button Done qui se trouve dans la barre d’outils est la suivante:

-(IBAction)btnClickedDone:(id)sender
{
    [self.view endEditing:YES];
}
16
g212gs

J'ai trouvé la réponse par josebama comme étant la réponse la plus complète et la plus nette disponible dans ce fil.

Vous trouverez ci-dessous la syntaxe de Swift 4 :

func textView(_ textView: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool {
    let resultRange = text.rangeOfCharacter(from: CharacterSet.newlines, options: .backwards)
    if text.count == 1 && resultRange != nil {
        textView.resignFirstResponder()
        // Do any additional stuff here
        return false
    }
    return true
}
12
Puneet Kohli

Swift

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
    }
    return true
}

and configure

6

Utilisation du contrôleur de navigation pour héberger une barre permettant d’écarter le clavier:

dans le fichier .h:

UIBarButtonItem* dismissKeyboardButton;

dans le fichier .m:

- (void)viewDidLoad {
    dismissKeyboardButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissKeyboard)];
}

-(void)textViewDidBeginEditing:(UITextView *)textView {
    self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}

-(void)dismissKeyboard {
    [self.textField resignFirstResponder];
    [self.textView resignFirstResponder];
    //or replace this with your regular right button
    self.navigationItem.rightBarButtonItem = nil;
}
6
Alex Stone

Tout comme Matt comment to samvermette, je n’aime pas non plus l’idée de détecter "\ n". La touche "retour" existe pour une raison dans UITextView, à savoir aller à la ligne suivante.

La meilleure solution à mon avis consiste à imiter l'application de messagerie pour iPhone, qui consiste à ajouter une barre d'outils (et un bouton) sur le clavier.

J'ai eu le code de l'article suivant sur le blog:

http://www.iosdevnotes.com/2011/02/iphone-keyboard-toolbar/

Pas:

-Ajouter la barre d’outils à votre fichier XIB - définir la hauteur à 460

-Ajouter un élément de bouton de barre d’outils (s’il n’a pas déjà été ajouté). Si vous devez l'aligner à droite, ajoutez également un élément de bouton de barre flexible à XIB et déplacez l'élément de bouton de la barre d'outils.

-Créer une action qui lie votre élément de bouton à resignFirstResponder comme suit:

- (IBAction)hideKeyboard:(id)sender {
    [yourUITextView resignFirstResponder];
}

-Ensuite:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

- (void)keyboardWillShow:(NSNotification *)notification {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];

    CGRect frame = self.keyboardToolbar.frame;
    frame.Origin.y = self.view.frame.size.height - 260.0;
    self.keyboardToolbar.frame = frame;

    [UIView commitAnimations];
}

- (void)keyboardWillHide:(NSNotification *)notification {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3];

    CGRect frame = self.keyboardToolbar.frame;
    frame.Origin.y = self.view.frame.size.height;
    self.keyboardToolbar.frame = frame;

    [UIView commitAnimations];
}
5
friend

Ajouter un observateur dans viewDidLoad

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(textViewKeyPressed:) name: UITextViewTextDidChangeNotification object: nil];

puis utilisez le sélecteur pour vérifier "\ n"

-(void) textViewKeyPressed: (NSNotification*) notification {

  if ([[[notification object] text] hasSuffix:@"\n"])
  {
    [[notification object] resignFirstResponder];
  }
}

Il utilise "\ n" et ne vérifie pas spécifiquement une clé de retour, mais je pense que c'est OK.

UPDATE

Voir la réponse de ribto ci-dessous qui utilise [NSCharacterSet newlineCharacterSet] à la place de \n

5
ToddB

Juste résolu ce problème d'une manière différente.

  • Créer un bouton qui sera placé à l'arrière-plan
  • Dans l'inspecteur d'attributs, définissez le type de bouton sur personnalisé et rend le bouton transparent.
  • Développez le bouton pour couvrir la totalité de la vue et assurez-vous qu'il est derrière tous les autres objets. Pour ce faire, faites glisser le bouton en haut de la liste dans la vue.
  • Faites glisser le bouton dans le fichier viewController.h et créez une action (Evénement envoyé: retouche à l'intérieur) comme:

    (IBAction)ExitKeyboard:(id)sender;
    
  • Dans ViewController.m devrait ressembler à:

    (IBAction)ExitKeyboard:(id)sender {
        [self.view endEditing:TRUE];
    }
    
  • Exécutez l'application et lorsque vous cliquez loin de TextView, le clavier disparaît
5
Uvichy

Essaye ça :

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if ([text isEqualToString:@"\n"]) {
        [self.view endEditing:YES];
    }

    return YES;

}
4
Nabil El

// Vous pouvez utiliser ceci ...

Étape 1. La première étape consiste à vous assurer de déclarer le support du protocole UITextViewDelegate. Ceci est fait dans votre fichier d'en-tête, comme exemple, voici l'en-tête appelé

EditorController.h:

@interface EditorController : UIViewController  {
  UITextView *messageTextView;
}

@property (nonatomic, retain) UITextView *messageTextView;

@end

Étape 2. Ensuite, vous devrez enregistrer le contrôleur en tant que délégué de UITextView. En reprenant l'exemple ci-dessus, voici comment j'ai initialisé la UITextView avec EditorController en tant que délégué…

- (id) init {
    if (self = [super init]) {
        // define the area and location for the UITextView
        CGRect tfFrame = CGRectMake(10, 10, 300, 100);
        messageTextView = [[UITextView alloc] initWithFrame:tfFrame];
        // make sure that it is editable
        messageTextView.editable = YES;

        // add the controller as the delegate
        messageTextView.delegate = self;
    }

Etape 3. Et maintenant, la dernière pièce du puzzle consiste à prendre des mesures en réponse au message shouldCahngeTextInRange comme suit:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range 
  replacementText:(NSString *)text
{
    // Any new character added is passed in as the "text" parameter
    if ([text isEqualToString:@"\n"]) {
        // Be sure to test for equality using the "isEqualToString" message
        [textView resignFirstResponder];

        // Return FALSE so that the final '\n' character doesn't get added
        return FALSE;
    }
    // For any other character return TRUE so that the text gets added to the view
    return TRUE;
}
4
Abhishek Pathak

Code rapide

Implémentez UITextViewDelegate dans votre classe/vue comme suit:

class MyClass: UITextViewDelegate  { ...

définir le délégué textView sur auto

myTextView.delegate = self

Et puis implémentez ce qui suit:

  func textViewDidChange(_ textView: UITextView) {
    if textView.text.characters.count >= 1 {

        if let lastChar = textView.text.characters.last {

            if(lastChar == "\n"){

              textView.text = textView.text.substring(to: textView.text.index(before: textView.text.endIndex))
              textView.resignFirstResponder()
            }
        }
    }
}

EDIT J'ai mis à jour le code car ce n'est jamais une bonne idée de modifier la saisie de l'utilisateur dans un champ de texte par une solution de contournement et de ne pas réinitialiser l'état une fois le code de hack terminé.

4

D'accord. Tout le monde a donné des astuces mais je pense que la bonne façon d’y parvenir est de

Connexion de l'action suivante à l'événement "Did End On Exit" dans Interface Builder. (Cliquez avec le bouton droit sur la TextField et faites glisser tout en maintenant la touche 's'est terminée à la sortie.' vers la méthode suivante.

-(IBAction)hideTheKeyboard:(id)sender
{
    [self.view endEditing:TRUE];
}
3
carbonr

Réponse rapide:

override func viewDidLoad() {
    super.viewDidLoad()
    let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
    view.addGestureRecognizer(tapGestureReconizer)
}

func tap(sender: UITapGestureRecognizer) {
    view.endEditing(true)
}
3
Glauco Neves

Vous pouvez également masquer le clavier lorsque vous touchez dans l'écran de visualisation:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch * touch = [touches anyObject];
     if(touch.phase == UITouchPhaseBegan) {
        [txtDetail resignFirstResponder];
      }
 }
3
Hitesh Vaghela

J'ai utilisé ce code pour changer de répondeur.

 - (BOOL)textView:(UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text
    {
        if ([text isEqualToString:@"\n"]) {
            //[textView resignFirstResponder];
            //return YES;
            NSInteger nextTag = textView.tag + 1;
            // Try to find next responder
            UIResponder* nextResponder = [self.view viewWithTag:nextTag];
            if (nextResponder) {
                // Found next responder, so set it.
                [nextResponder becomeFirstResponder];
            } else {
                // Not found, so remove keyboard.
                [textView resignFirstResponder];
            }
            return NO; 


            return NO;
        }
        return YES;

    }
3
Avijit Nagare

La question demande comment faire avec la touche de retour, mais je pense que cela pourrait aider quelqu'un avec l'intention de simplement faire disparaître le clavier lors de l'utilisation de UITextView:

@IBOutlet weak var textView: UITextView!

private func addToolBarForTextView() {
  let textViewToolbar: UIToolbar = UIToolbar()
  textViewToolbar = UIBarStyle.Default
  textViewToolbar = [
    UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Done, target: self, action: #selector(self.cancelInput)),
    UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil),
    UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: #selector(self.doneInput))
  ]
  textViewToolbar()
  self.textView.inputAccessoryView = textViewToolbar //do it for every relevant textView if there are more than one
}

func doneInput() {
  self.textView.resignFirstResponder()
}

func cancelInput() {
  self.textView.text = ""
  self.textView.resignFirstResponder()
}

Appelez addToolBarForTextView () dans viewDidLoad ou une autre méthode de cycle de vie.

Il semble que c'était la solution parfaite pour moi.

À votre santé,

Murat

1
Murat Yasar
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range  replacementText:(NSString *)text
{
    if (range.length==0) {
        if ([text isEqualToString:@"\n"]) {
            [txtView resignFirstResponder];
            if(textView.returnKeyType== UIReturnKeyGo){

                [self PreviewLatter];
                return NO;
            }
            return NO;
        }
    }   return YES;
}
1
Rinju Jain
+ (void)addDoneButtonToControl:(id)txtFieldOrTextView
{
    if([txtFieldOrTextView isKindOfClass:[UITextField class]])
    {
        txtFieldOrTextView = (UITextField *)txtFieldOrTextView;
    }
    else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
    {
        txtFieldOrTextView = (UITextView *)txtFieldOrTextView;
    }

    UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,
                                                                          0,
                                                                          [Global returnDeviceWidth],
                                                                          50)];
    numberToolbar.barStyle = UIBarStyleDefault;


    UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"btn_return"]
                                                                style:UIBarButtonItemStyleBordered
                                                               target:txtFieldOrTextView
                                                               action:@selector(resignFirstResponder)];

    numberToolbar.items = [NSArray arrayWithObjects:btnDone,nil];
    [numberToolbar sizeToFit];

    if([txtFieldOrTextView isKindOfClass:[UITextField class]])
    {
         ((UITextField *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
    }
    else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
    {
         ((UITextView *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
    }
}
1
Venu Gopal Tewari

Vous devez ajouter UIToolbar en haut de la page UITextView pour simplifier la tâche plutôt que d'utiliser shouldChangeTextIn

Dans Swift 4

let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        toolbar.barStyle = .default
        toolbar.items = [
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
            UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneAction))
        ]
        textView.inputAccessoryView = toolbar
@objc func doneAction(){
 self.textView.resignFirstResponder()
}
0
Giang

Je sais que ce n'est pas la réponse exacte à cette question, mais j'ai trouvé ce fil après avoir recherché une réponse sur Internet. Je suppose que les autres partagent ce sentiment.

C’est là ma variante du UITapGestureRecognizer que je trouve fiable et facile à utiliser - il suffit de définir le délégué de TextView sur ViewController.

Au lieu de ViewDidLoad, j'ajoute UITapGestureRecognizer lorsque TextView devient actif pour modification:

-(void)textViewDidBeginEditing:(UITextView *)textView{
    _tapRec = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];

    [self.view addGestureRecognizer: _tapRec];
    NSLog(@"TextView Did begin");
}

Lorsque je tape en dehors de TextView, la vue termine le mode d'édition et le UITapGestureRecognizer se supprime de lui-même afin que je puisse continuer à interagir avec les autres contrôles de la vue.

-(void)tap:(UITapGestureRecognizer *)tapRec{
    [[self view] endEditing: YES];
    [self.view removeGestureRecognizer:tapRec];
    NSLog(@"Tap recognized, tapRec getting removed");
}

J'espère que ça aide. Cela semble tellement évident, mais je n'ai jamais vu cette solution sur le Web. Est-ce que je fais quelque chose de mal?

0
BGC

N'oubliez pas de définir le délégué pour textView - sinon, resignfirstresponder ne fonctionnera pas.

0
Ron Holmes

Essaye ça .

NSInteger lengthOfText = [[textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];
0
Hiren Panchal

Pour Xcode 6.4., Swift 1.2. :

   override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        super.touchesBegan(touches, withEvent: event)
        if let touch = touches.first as? UITouch
        {
            self.meaningTextview.resignFirstResponder()
        }
    }
0
MB_iOSDeveloper