26

I have filled my view with ScrollView (same size as the view) and I'm stuck at how to resign first responder when user tap elsewhere in the View (or the scrollview). Any idea on how to do that ? I'm using the following method but it's not working as expected:

- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

Thx for helping,

Stephane

Steve
  • 4,562
  • 8
  • 43
  • 83

18 Answers18

85

I found the answer here:

If your ultimate aim is just to resign the first responder, this should work: [self.view endEditing:YES]

The endEditing(_:) method is designed right for it

Causes the view (or one of its embedded text fields) to resign the first responder status.

Eugene Berdnikov
  • 1,852
  • 2
  • 19
  • 28
DanSkeel
  • 3,536
  • 32
  • 53
  • 3
    This is a much better solution than calling each individual input's `resignFirstResponder` method. – Tom Redman Nov 15 '12 at 18:15
  • But where put this line of code, so that a tap anywhere (outside of field being edited) executes it? – ToolmakerSteve Mar 14 '17 at 06:53
  • @ToolmakerSteve e.g. you can add this code to the method that handles tap of `UITapGestureRecognizer` added to ViewController's view. – DanSkeel Mar 14 '17 at 15:59
  • @AlexZavatone You just put it where you need it to go. If you want to do it when the view disappears, put it in `viewDidDisappear`. Or perhaps you want to do it when a button is clicked, so you'd put it in the code for your button action. – Adrian Sep 25 '17 at 19:49
18

For a more robust and clean solution add a tap gesture recognizer to your primary view.

This will work better with nested views and will be cleaner than secret buttons in code and UI builder.

In your view did load:

UITapGestureRecognizer* tapBackground = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard:)];
[tapBackground setNumberOfTapsRequired:1];
[self.view addGestureRecognizer:tapBackground];

..and define your target action to be triggered on tap:

-(void) dismissKeyboard:(id)sender
{
    [self.view endEditing:YES];
}
Andres Canella
  • 3,592
  • 1
  • 30
  • 45
11

The best option is the shortest way ;)

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}
Onder OZCAN
  • 1,496
  • 1
  • 16
  • 33
10

For Swift 3 and Swift 4 you can do this:

func viewDidLoad() {
    super.viewDidLoad()

    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.hideKeyboardByTappingOutside))

    self.view.addGestureRecognizer(tap)
}

@objc func hideKeyboardByTappingOutside() {
    self.view.endEditing(true)
}
pableiros
  • 10,752
  • 11
  • 79
  • 78
4

UIViewController inherits from UIResponder so a naive way (I am pretty sure is not the smartest way to do it) would be by overwriting the following methods (at least 1 of them)

– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
- touchesCancelled:withEvent:

Next you could get the touched view by doing

UITouch *touch = [touches anyObject];
UIView *touchedView = [touch view];

finally resign the first responder if that view is not your text field

if(touchedView != textField){
    [textField resignFirstResponder];
}

_

Demerits of this approach:

You will have to handle the "tap" by yourself. (Same problem as the old iOS 3.1 or earlier). You will have to come up with your own implementation to differentiate single taps from drags, swipes, double taps, long taps, etc. Is not hard to get it working well but it is not likely you get it exactly the same way Apple detects taps (timings, distances, thresholds count!) However, that depends on your needs.

If your view structure is simple enough then you could add a gesture recognizer to the container view and resign the first responder every time the handler is called :)

Hope this helps

nacho4d
  • 39,335
  • 42
  • 151
  • 231
3
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [self.TextField resignFirstResponder];

    return YES;
}
Deepakraj Murugesan
  • 1,305
  • 11
  • 28
3

Nobody has yet presented the best answer here:

[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];

From the documentation on this method:

Normally, this method is invoked by a UIControl object that the user has touched. The default implementation dispatches the action method to the given target object or, if no target is specified, to the first responder. Subclasses may override this method to perform special dispatching of action messages.

I bolded the relevant part of that paragraph. By specifying nil for the to: parameter, we automatically call the given selector on the first responder, wherever it is, and we don't need to have any knowledge of where in the view hierarchy it is. This can also be used to invoke other methods on the first responder as well, not just cause it to resign first responder status.

Gavin
  • 8,039
  • 3
  • 28
  • 40
  • Although this may (note may as I am not sure) take a little longer to execute than other methods it is very handy when needing to resign the firstResponder from a Class where the current VC is not known and so is probably quicker than finding it and so on. – Recycled Steel Nov 17 '15 at 23:41
  • @RecycledSteel - given that iOS must always know the current firstResponder, I see no reason why this would be slow. – ToolmakerSteve Mar 14 '17 at 06:50
2

Give a unique Tag to your UITextfield (i.e. 333), then to resign the first responder do this:

UITextField *textField = (UITextField *)[self.view viewWithTag:333];
[textField resignFirstResponder];
Lefteris
  • 14,002
  • 2
  • 52
  • 90
1
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(TapTodissmiss)];

[self.view addGestureRecognizer:tap];

and selector

-(void)TapTodissmiss{

    [self.txtffld resignFirstResponder];

}
Ghanshyam Tomar
  • 724
  • 1
  • 8
  • 23
1

I am posting this as a separate answer because a lot of the other answers recommend using [textField resignFirstResponder]. This is causing an invalid capability error in Xcode 11.5 with the incomprehensible addition "Unable to insert COPY_SEND"

On a iPad with iOS 9.3.6:

2020-05-23 20:35:01.576 _BSMachError: (os/kern) invalid capability (20)
2020-05-23 20:35:01.580 _BSMachError: (os/kern) invalid name (15)

On a iPad with iPadOS 13.5:

2020-05-23 20:38:49 [Common] _BSMachError: port 12f0f; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"

On a iPhone with iOS 13.5:

2020-05-23 20:43:34 [Common] _BSMachError: port d503; (os/kern) invalid capability (0x14) "Unable to insert COPY_SEND"

[textField resignFirstResponder] cannot be used anymore.

Jan Ehrhardt
  • 315
  • 1
  • 5
  • 16
1

Just create an IBOutlet or pointer to the text field and call [textField resignFirstResponder]; from where ever you want.

Or Ron
  • 2,243
  • 19
  • 34
  • IBOulet is to access interface object properties. What I want is to let my view controller know when a specific event occurs on a text field. An event like "mouseout" when using Javascript. Objective C seems poor at these simple things lol – Steve Oct 17 '11 at 14:15
  • in order to resign first responder, that is exactly what u need an IBOutlet for the textfield. If you want to get this method called you should set your viewcontroller as the textFields delegate. – Or Ron Oct 17 '11 at 14:40
  • Furthermore, sometimes users will want to exit from "editing" mode without pressing any button, they are used to it. you should catch touches on the background and use again either IBOutlet or pointer to this text field and make him resign first responder. – Or Ron Oct 17 '11 at 14:41
  • And by the way, the point of this button is to vote whether my comment is totally irrelevant or nonsense and not if you believe I misinterpreted your question. Next time just learn to leave a comment. – Or Ron Oct 17 '11 at 14:47
  • Thx for commeting. It's not me who downvoted your reply! Sorry for late reply to your comments The thing is IBOulet does not respond to events but IBAction do. What I'm looking is an event/delegate method. – Steve Oct 17 '11 at 15:25
0

When possible the best option is to make your UIView to inherit from UIButton instead.

You just need to change it in Interface Builder and immediately you will get the options to create an Action "Touch Up Inside" like any other button.

Your view will work as usual because UIButton is still a view but you will get notified when someone taps on the view. There you can call

[self.view endEditing:YES];

To resign any keyboard.

Please note that if your view is an UIScrollView this solution is not for you.

Julio Bailon
  • 3,285
  • 2
  • 30
  • 33
0

Create your IBOutlet in ViewController.h file like below:

//YOUR ViewController.h FILE

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    //WIRE THIS WITH YOUR UITextField OBJECT AT YOUR .xib FILE
    IBOutlet UITextField *yourIBOutletUITextField;
}

@end

Add this to your ViewController.m file:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
     [yourIBOutletUITextField resignFirstResponder];
}

like below:

//YOUR ViewController.m FILE

#import "ViewController.h"

@implementation ViewController


//ADD THIS METHOD BELOW
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
     [yourIBOutletUITextField resignFirstResponder];
}


- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    }

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

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

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

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

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

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

@end
EFE
  • 3,512
  • 4
  • 20
  • 25
0

For my implementation, [self.view endEditing:YES] did not work.

To finally make sure the keyboard was hidden, I had to make one of my views the first responder, and then immediately make it resign as per the function below:

-(void) hideKeyboardPlease{
   [uiTextTo becomeFirstResponder];
   [uiTextTo resignFirstResponder];
}
Lenny
  • 313
  • 4
  • 17
0

This is what I do...

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideAllKeyboards)];
tapGesture.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tapGesture];

-(void) hideAllKeyboards {
    [textField_1 resignFirstResponder];
    [textField_2 resignFirstResponder];
    [textField_3 resignFirstResponder];
    .
    .
    .
    [textField_N resignFirstResponder];
}
Fahim Parkar
  • 28,922
  • 40
  • 153
  • 260
0

This is the easiest way iv found that works consistently. Just add a tap gesture recognizer on the view and you're good to go.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

[self.view endEditing:YES];

}

Andrew Cook
  • 116
  • 1
  • 7
0

Update

I found another simple way

simply declare a property :-

@property( strong , nonatomic) UITextfield *currentTextfield;

and a Tap Gesture Gecognizer:-

@property (strong , nonatomic) UITapGestureRecognizer *resignTextField;

In ViewDidLoad

_currentTextfield=[[UITextField alloc]init];
_resignTextField=[[UITapGestureRecognizer alloc]initWithTarget:@selector(tapMethod:)];

[self.view addGestureRecognizer:_resignTextField];

Implement the textfield delegate method didBeginEditing

 -(void)textFieldDidBeginEditing:(UITextField *)textField{


      _currentTextfield=textField;

    }

Implement Your Tap Gesture Method (_resignTextField)

 -(void)tapMethod:(UITapGestureRecognizer *)Gesture{

     [_currentTextfield resignFirstResponder];

 }
tryKuldeepTanwar
  • 3,112
  • 1
  • 16
  • 45
-5

Create a big button behind it all, and the button's callback calls resignFirstResponder to every input you have. Just make sure to put everything that the user interacts with on top of the button.

Of course the button will be custom rect and transparent.

I would look into implementing an IBOutletCollection called resignables and set every input to that collection. And the button callback iterates over that collection calling resignFirstResponder to them all.

Edit: if it is a re-sizable scroll view, remember to correctly set the re-size markers in the button's view option, that way the button will always expand and contract to the scrollview's height and width

Sergio Campamá
  • 716
  • 7
  • 14
  • 1
    The correct solution is offered by DanSkeel below, and really should be marked as the accepted answer. – strange May 14 '14 at 20:22