2

I have a UITableView with an associated UITableViewController. However, I've modified the table to also have a view with a textfield subview.

As always, I want the keyboard to disappear when the user hits 'done' (easy) and when they touch anywhere else on screen other than the textfield (hard, stuck!).

The normal way to achieve this is to change the class to UIControl so it can handle actions... but I can't do this for my UITableView/UITableViewController combination.

How can I solve this problem?

ConfusedNoob
  • 9,826
  • 14
  • 64
  • 85

4 Answers4

6

U can handle user touches by adding a UITapGestureRecognizer to your view.
For example if u don't want to enable row selection in your tableView u call self.tableView.allowsSelection = NO;
But if u still want to detect user touches u add a UITapGestureRecognizer to your tableView (or to tableView.superview).
U can have more control if u implement the UIGestureRecognizerDelegate, this way u can detect and then choose witch touches to receive and witch not.
To do that just add this code to your UITableViewController:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.tableView.allowsSelection = NO;

    UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(viewTapped:)];
    tgr.delegate = self;
    [self.tableView addGestureRecognizer:tgr]; // or [self.view addGestureRecognizer:tgr];
    [tgr release];
}    

- (void)viewTapped:(UITapGestureRecognizer *)tgr
{
    NSLog(@"view tapped");  
    // remove keyboard
}

// this is optional, it let u choose witch touches to receive, for example here I'm checking if user has tapped on a textField

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isKindOfClass:[UITextField class]]) {
        NSLog(@"User tapped on UITextField");
    }
    return YES; // do whatever u want here
}
Eyal
  • 10,777
  • 18
  • 78
  • 130
  • This worked great. The only thing that seemed strange is I had to call resignFirstResponder on the textField and not on the view which normally works to clear the keyboard. – ConfusedNoob Aug 04 '12 at 22:49
  • 1
    You usually call resignFirstResponder on the textField not the view, but if u really want to something with the view (probably not) u can call [self.view endEditing], this I believe will also do the trick... – Eyal Aug 04 '12 at 23:09
  • 2
    As noted by @Matt Rees on this other thread (http://stackoverflow.com/a/11142038/285853), you need to set tapGesture.cancelsTouchesInView = NO; if you want other views to keep receiveing touches as usual. – Miros Mar 17 '14 at 14:50
  • You also need to declare your viewController to be UIGestureRecognizerDelegate if you add tgr.delegate = self – Frederic Adda Sep 01 '15 at 10:50
4

A normal practice is to put a custom UIButton( it becomes visible only when uitextfield begins editing ) behind keyboard view, and when user clicks on screen he actually clicks on that button, and associated selector can resign first responder.

-(void) closeKeyboard:(UIButton *) b {
    [self.view endEditing:YES]; //assuming self is your top view controller.
    [b setHidden:YES];  
}

Using endEditing is better, cause it loops through all subviews and looks for current first responder.

2

Using alloc breaks with ARC enabled

Just add the following to the viewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    //where text field is a @property (nonatomic,retain) IBOutlet to your textfield
    [textField resignFirstResponder]; 
}
PicoCreator
  • 9,886
  • 7
  • 43
  • 64
0

When a row is tapped the didSelectRowAtIndexPath is called. If a textField located inside inside a row is tapped then the textfield delegate is called.

So in addition to your done button method, in didSelectRowAtIndexPath add a check for the text field being first responder and ask it to resign. Assuming a the selected indexPath is not the row of the textfield.

Olaf
  • 3,042
  • 1
  • 16
  • 26
  • I don't want rows in the table to be selectable.. so I don't think this approach will work. – ConfusedNoob Aug 04 '12 at 16:49
  • Set the selection color equal to the background color in cellForRowAtIndexpath. In didSelectRowAtIndexPath immediately deselect any selected row. The effect should be what you are after – Olaf Aug 05 '12 at 14:26