hide the keyboard when touched outside for tableView [duplicate] - iphone

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Dismiss keyboard on touch anywhere outside UITextField
MyViewController in interface builder :
In my custom TableViewCell I have a TextField. My plan is disappear KeyPad when User Clicks outside the touchpad.
I could not make it works using UITapGestureRecognizer
in viewDidLoad:
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)];
[self.tableView addGestureRecognizer:gestureRecognizer];
Then how should I make it work with cell.TextField ?
I read about this solution here:
[self.view endEditing:YES];
or
[[self.tableView superView] endEditing];
I couldn't make it work neither. Where should I use them?
Or if you have any better way, please let me know.
Edit
I could not make it work:
#property (nonatomic, strong) UITextField *cellTextField;
in viewDidLoad:
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideKeyboard)];
[self.settingTableView addGestureRecognizer:gestureRecognizer];
And we have:
- (void) hideKeyboard {
[self.cellTextField resignFirstResponder];
}
And :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
SettingTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
self.cellTextField = cell.dataEdit;
What is my fault?

you could add a property to your view controller, say
#property (nonatomic, strong) UITextField *cellTextField
and then assign cell.TextField to it.
in hideKeyboard: you can dismiss the keyboard by doing
[cellTextField resignFirstResponder];
You should call resignFirstResponder on the textField itself, not the controller's view.

Add [cellTextField resignFirstResponder]; hope this works...
Maybe look at UITextFieldDelegate, and in particular - (BOOL)textFieldShouldReturn:(UITextField *)textField

You Can hide Keyboard if You Put the Whole Screen Custom UIButton.Then Attached this Method With this Custom UIButton.set the UITextField tag =1 in Interface builder before implementation of this method.
-(IBAction)HideKeyboard
{
[[self.view viewWithTag:1]resignFirstResponder];
}
Note: if You Want to hide keyboard by Click in CustomCell then Put this UIButton there or You Can Call this Method using Gesture Recognization as you did in Your Code without Using Custom UIButton.
And also make sure You Connect UITextField delegate.Hope this Work .

You may try this.
- (void) hideKeyboard {
[self.view endEditing:YES];
}

You can achieve this task by inserting the following method
You must implement UITextFieldDelegate
you must set a tag for the textfield in the storyboard or in when you start loading the cells of the table
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
self.currentIndexPath = [(UITableView*)textField.superview.superview.superview indexPathForCell:(UITableViewCell*)textField.superview.superview];
self.currentTableView = (UITableView*)textField.superview.superview.superview;
self.keyboardIsShown = YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
if(self.keyBoardIsShown)
{
[self dismissKeyboard:currentPoint UsedTableView:self.myTableView IndexPathForRow:self.currentIndexPath TexFieldTag:tag];
self.keyBoardIsShown = NO;
}
}
-(void)dismissKeyboard:(CGPoint)currentPoint UsedTableView:(UITableView*)table IndexPathForRow:(NSIndexPath*)indexPath TexFieldTag:(int)tag
{
UITableViewCell *cell = [table cellForRowAtIndexPath:indexPath];
if(!CGRectContainsPoint([self.view convertRect:[cell viewWithTag:tag].frame fromView:[cell viewWithTag:tag].superview.superview], currentPoint))
{
[(UITextField*)[cell viewWithTag:tag] resignFirstResponder];
}
}

Related

How to handle events (Touch Up Inside/Outside) for UItableViewCell with a text field?

I have a custom UITableViewCell which has a text field inside it. I have created it using IB and have a custom class with it.
Now, my issue is that I want to setup the text field so that during text entry if the user clicks outside the text field (without hitting the return/done key on the keypad), the field resigns first responder. I understand, that do that I need to handle the Touch Up Inside Event. However my tableview class never receives this event even though I have done the connections. I am assuming that its because its not subclass of UIcontrol, which I cant make it as it needs to be UITableViewCel.
So whats the solution?? How do I receive these events??
Header File:
#import <UIKit/UIKit.h>
#interface MMSingleTextFieldCell : UITableViewCell <UITextFieldDelegate>
// Properties
#property (weak, nonatomic) IBOutlet UITextField *singleTextField;
// Methods
- (IBAction)eventTouchUpOutside:(id)sender;
- (IBAction)eventTouchUpInside:(id)sender;
#end
Class File:
#import "MMSingleTextFieldCell.h"
#implementation MMSingleTextFieldCell
#synthesize singleTextField;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (IBAction)eventTouchUpOutside:(id)sender {
[singleTextField resignFirstResponder];
}
- (IBAction)eventTouchUpInside:(id)sender {
[singleTextField resignFirstResponder];
}
I have just recently open sourced a project on Github that should make this all relatively easy to do. It includes a class that can be easily inserted into a cell and a sample project demonstrating its capabilities.
If you look in RootTableViewController's viewDidLoadMethod you will see that I am adding a gesture recognizer:
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(dismissKeyboard)];
_tapGestureRecognizer.delegate = self;
_tapGestureRecognizer.cancelsTouchesInView = NO;
[self.tableView addGestureRecognizer:_tapGestureRecognizer];
Add the dismiss keyboard method:
- (void)dismissKeyboard {
[_textField resignFirstResponder];
}
Add a gesture recognizer callback (in RootTableViewController):
//see: http://stackoverflow.com/questions/7195661/why-is-uigesturerecognizer-being-called-on-my-textfield-clear-button
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if([touch.view isKindOfClass:[UITextField class]] ||
[touch.view isKindOfClass:[UIButton class]])
{
return NO;
}
return YES;
}
Of course, this means you have to make RootTableViewController adhere to the UIGestureRecognizerDelegate protocol (in the header file):
#interface RootTableViewController : UITableViewController<SATextFieldDelegate, UIGestureRecognizerDelegate>
If you want the user to scroll the table view and dismiss the keyboard implement the following table view delegate callback:
- (void)scrollViewWillBeginDragging:(UIScrollView *)activeScrollView {
if (_textField.isFirstResponder) {
[self dismissKeyboard];
}
}
I believe this is the function you want.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if (_textField.isFirstResponder) {
[self dismissKeyboard];
}
}
Try this:
1) Implement the UIGestureRecognizerDelegate protocol
2) In viewDidLoad for example, create the following
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(hideKeyboard:)];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
3) Now, implement the method from protocol from 1
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
// Use this for allow some control to receive his natural tap event. You can use tap.cancelsTouchesInView = NO; in the 2 step instead of this, try both of then and choose on your own.
if (touch.view == <some control>) {
//NSLog(#"NO");
return NO;
}
//NSLog(#"YES");
return YES;
}
4) finally, implement the callback for tap
-(void) hideKeyboard:(id)sender{
if (<your edit text>.isEditing) {
[<your edit text> resignFirstResponder];
}
}
I hope this will help, or at least point you to the right direction

Showing UIMenuController loses keyboard

I'm making an iphone app similar to the Messages app that comes on the phone. I just set up the ability to copy messages via a UIMenuController, but if the keyboard is showing and someone tries to copy a message, the keyboard goes away (presumably because of my [cell becomeFirstResponder]; where cell is the message cell being copied).
Is there a way to show the Copy message without losing the keyboard?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath {
//...other cell setup stuff...
UILongPressGestureRecognizer *longPressGesture =
[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(showCopyDialog:)];
[cell addGestureRecognizer:longPressGesture];
return cell;
}
- (void)showCopyDialog:(UILongPressGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
ConvoMessageCell *cell = (ConvoMessageCell *)[gesture view];
NSIndexPath *indexPath = [self.tblConvo indexPathForCell:cell];
UIMenuController *theMenu = [UIMenuController sharedMenuController];
[cell becomeFirstResponder];
[theMenu setTargetRect:CGRectMake(menuX, menuY, 100, 100) inView:cell];
[theMenu setMenuVisible:YES animated:YES];
}
}
I solved this dilemma by subclassing UITextView to provide a way to override the nextResponder and disable the built-in actions (Paste), like so:
#interface CustomResponderTextView : UITextView
#property (nonatomic, weak) UIResponder *overrideNextResponder;
#end
#implementation CustomResponderTextView
#synthesize overrideNextResponder;
- (UIResponder *)nextResponder {
if (overrideNextResponder != nil)
return overrideNextResponder;
else
return [super nextResponder];
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (overrideNextResponder != nil)
return NO;
else
return [super canPerformAction:action withSender:sender];
}
#end
Then, in your gesture action handler, check whether the text view is already the first responder. If so, have it override the next responder; otherwise the keyboard is probably hidden anyway and you can simply becomeFirstResponder. You'll also have to reset the override when the menu hides:
if ([inputView isFirstResponder]) {
inputView.overrideNextResponder = self;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(menuDidHide:)
name:UIMenuControllerDidHideMenuNotification object:nil];
} else {
[self becomeFirstResponder];
}
- (void)menuDidHide:(NSNotification*)notification {
inputView.overrideNextResponder = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIMenuControllerDidHideMenuNotification object:nil];
}
Using the table view delegate methods introduced in iOS 5 (shouldShowMenuForRowAtIndexPath etc.) wasn't a solution for me as I needed control over the positioning of the menu (by default it's simply horizontally centered over the cell, but I'm displaying message bubbles and wanted the menu centered over the actual bubble).
In iOS 5, you can now use the table view delegate methods to show the Menu Controller:
- (BOOL) tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender;
Showing the Menu Controller in this manner will not resign the keyboard.
I'm still curious about this though as I have an app that supports pre-iOS 5 that I would like to do what you're saying also (not resign the keyboard when the copy menu appears).

Custom accessory view button - wrong row selected

I have implemented the accessory view of a UITableViewCell as a button, in the
button's selector method i have the following code
- (UIButton *) makeDetailDisclosureButton
{
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage* image = [UIImage imageNamed:#"custom.png"];
[button setImage:image forState:UIControlStateNormal];
button.frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
[button addTarget: self
action: #selector(accessoryButtonTapped:withEvent:)
forControlEvents: UIControlEventTouchUpInside];
return ( button );
}
- (void) accessoryButtonTapped: (UIControl *)button withEvent:(UIEvent *) event
{
NSSet *touches = [event allTouches];
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:currentTouchPosition];
if (indexPath != nil)
{
[self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}
}
However sometimes the wrong row is being selected, ie if I tap on row 0 in the table i get
row = 1 actually being selected. The code above is quite well known as a solution for
a custom accessory view but it is proving unreliable. Is there something else i need to do here?
Try this,
- (void) accessoryButtonTapped: (UIControl *)button withEvent:(UIEvent *) event
{
UITableViewCell *cell = (UITableViewCell*)button.superview.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}
The idea here is pretty basic. Button's superview should give me cell.contentView and it's superview is the cell. There is lovely helper method indexPathForCell: which will give us the index path which we can use to pass to the delegate method.
Nothing wrong seen in your code but you can refer this tutorial for reference.
Difference I see in your code and in tutorial is
- (void) accessoryButtonTapped: (UIControl *)button withEvent:(UIEvent *) event //Your event
- (void)checkButtonTapped:(id)sender event:(id)event //In tutorial.
This does not make difference but still just a thought.
Hope it helps.
What do you have at tableView:accessoryButtonTappedForRowWithIndexPath: method?
Anyhow, I wouldn't mess with the touches to get the index path.
Instead I'd tag the button with the [indexPath row] and then pass the tag to the other method.
I had a similar problem: The index path row in tableView:accessoryButtonTappedForRowWithIndexPath: was consistently wrong for -- in my case -- every 5th row of the table view.
e.g., tapping the Detail Disclosure button for row 4 correctly gave me indexPath.row = 3; but tapping row 5 also gave me indexPath.row = 3.
My mistake related to having set incompatible row heights for my custom UITableViewCell.
i.e., I set the custom row height to 56 in the UITableViewCell's Size Inspector, but I forgot that I earlier had set the row height programmatically in tableView:heightForRowAtIndexPath: as a multiple of tableView.rowHeight. The default tableView.rowHeight (editable in the UITableView's Size Inspector) is 44, and that 'clash' apparently caused the problem.
I resolved my problem by setting the row height fields to 56 in the Size Inspectors of both the UITableView & the UITableViewCell and commenting out the tableView:heightForRowAtIndexPath: method.

How to get keyboard to disappear?

I have a text field that is being shown in a UITableViewCell and I want to be able to hide the keyboard when the user touches anywhere else on the screen aside from the text field. I know about [field resignFirstResponder];, but I don't know how to intercept touches on the background of the UITableView in order to call "resignFirstResponder".
Any help would be greatly appreciated.
The only way to do this is to subclass the UITableView, implement the touchesBegan method in your subclassed UITableView and send your UITextField objects to the UITableView. Here's how it should look like -
// GRTableView.h
// StackOverflow Example
//
// Created by Raphael Caixeta on 8/13/10.
// Copyright 2010 Raphael Caixeta. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface GRTableView : UITableView <UITableViewDelegate> {
UITextField *textField;
}
#property(nonatomic, retain) UITextField *textField;
#end
//
// GRTableView.m
// StackOverflow Example
//
// Created by Raphael Caixeta on 8/13/10.
// Copyright 2010 Raphael Caixeta. All rights reserved.
//
#import "GRTableView.h"
#implementation GRTableView
#synthesize textField;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[textField resignFirstResponder];
[super touchesBegan:touches withEvent:event];
}
- (void)dealloc {
[super dealloc];
}
#end
And then in your regular file where you'll allocate a UITableView, just allocate the subclassed view and pass your textfield to the subclass. Hope that helps.
Try implementing the - (void)textFieldDidEndEditing:(UITextField *)textField method of the UITextFieldDelegate.
It's very simple: create a transparent UIView object that receives touches in the area you want, and when the touch is in the bounds of that view, call resignFirstResponder.
// somewhere in a view controller
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[self.view addSubview:backgroundView];
// in the touchesBegan:withEvent: method, for example
UITouch *touch = [[event allTouches] anyObject];
if ([field isFirstResponder] && [touch view] == backgroundView) {
[field resignFirstResponder];
}
Alternatively, you could skip the backgroundView stuff and just add a conditional statement like the following in your touchesBegan:withEvent: method:
UITouch *touch = [[event allTouches] anyObject];
if ([field isFirstResponder] && [touch view] != field) {
[field resignFirstResponder];
}
If the touch is ! (not) in the bounds of field, then you want to remove the keyboard.
You can hide the key board in following conditions,
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { }
(void)TextField:(UITextField *)TxtFld textDidChange:(NSString *)searchText{ }
In this two condition you should place [TxtFld resignFirstResponder]; And also when your work for textfield is over you are performing some actions on that event also you should write [TxtFld resignFirstResponder]; where TxtFld is an object for UIText field.
By using this thing the keyboard will dismissed. The touch method you are writing may not work on the UITableviewCell. so this will be the way to hide the key board.
still it not work then specify the exact problem occurs.
There's an easier way to do this than the standard "subclass UITableView" or "add a subclass of UIView and handle touches". I create a UIButton with a clear background color over the area that I want to handle touches. When I add it to the view, I set its hidden property to YES.
When the text field begins editing, I set the button's hidden property to NO.
Then the button simply responds to UITouchUpInside control event, in a method which calls [textField resignFirstResponder] and button.hidden = YES.

How can I keep track of the index path of a button in a table view cell?

I have a table view where each cell has a button accessory view. The table is managed by a fetched results controller and is frequently reordered. I want to be able to press one of the buttons and obtain the index path of that button's table view cell. I've been trying to get this working for days by storing the row of the button in its tag, but when the table gets reordered, the row becomes incorrect and I keep failing at reordering the tags correctly. Any new ideas on how to keep track of the button's cell's index path?
If you feel uncomfortable relying on button.superview, this method should be a little more robust than some of the other answers here:
UIButton *button = (UIButton *)sender;
CGRect buttonFrame = [button convertRect:button.bounds toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:buttonFrame.origin];
This stopped working with iOS 7; check out Mike Weller's answer instead
- (IBAction)clickedButton:(id)sender {
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview;
UITableView *tableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [tableView indexPathForCell:cell];
}
Or shorter:
- (IBAction)clickedButton:(id)sender {
NSIndexPath *indexPath = [(UITableView *)sender.superview.superview indexPathForCell:(UITableViewCell *)sender.superview];
}
Both are untested!
Crawling up view hierarchies with .superview (like all of the existing answers demonstrate) is a really bad way to do things. If UITableViewCell's structure changes (which has happened before) your app will break. Seeing .superview.superview in your code should set off alarm bells.
The button and its handler should be added to a custom UITableViewCell subclass and layed out there. That's where it belongs.
The cell subclass can then delegate out the button event through a standard delegate interface, or a block. You should aim for something like this:
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCustomCell *cell = ...;
// ...
cell.onButtonTapped = ^{
[self buttonSelectedAtIndexPath:indexPath];
}
// OR
cell.delegate = self;
// ...
}
(Note: if you go the block route, you will need to use a __weak self reference to prevent retain cycles, but I thought that would clutter up the example).
If you take the delegate route you would then have this delegate method to implement:
- (void)cellButtonPressed:(UITableViewCell *)cell
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
// ...
}
Your code now has full access to the appropriate context when it handles the event.
Implementing this interface on your cell class should be straightforward.
I don't know why I need to call the method superview twice to get the UITableViewCell.
Update:
Thank for Qiulang, now I got it.
"That's because SDK now has added a private class called UITableViewCellContentView for UITableViewCell, which is button's superview now." – Qiulang
UIButton *button = (UIButton *)sender;
UITableViewCell *cell = (UITableViewCell *)button.superview.superview;
UITableView *curTableView = (UITableView *)cell.superview;
NSIndexPath *indexPath = [curTableView indexPathForCell:cell];
I had this same issue also and built a simple recursive method that works no matter how many views deep you triggering control is.
-(NSIndexPath*)GetIndexPathFromSender:(id)sender{
if(!sender) { return nil; }
if([sender isKindOfClass:[UITableViewCell class]])
{
UITableViewCell *cell = sender;
return [self.tableView indexPathForCell:cell];
}
return [self GetIndexPathFromSender:((UIView*)[sender superview])];
}
-(void)ButtonClicked:(id)sender{
NSIndexPath *indexPath = [self GetIndexPathFromSender:sender];
}
I have created one Method for getting indexPath, Hope this will help you.
Create Button Action (aMethod:) in cellForRowAtIndexPath
-(void) aMethod:(UIButton *)sender
{
// Calling Magic Method which will return us indexPath.
NSIndexPath *indexPath = [self getButtonIndexPath:sender];
NSLog(#"IndexPath: %li", indexPath.row);
NSLog(#"IndexRow: %li", indexPath.section);
}
// Here is the Magic Method for getting button's indexPath
-(NSIndexPath *) getButtonIndexPath:(UIButton *) button
{
CGRect buttonFrame = [button convertRect:button.bounds toView:groupTable];
return [groupTable indexPathForRowAtPoint:buttonFrame.origin];
}
Use this Perfect working for me.
CGPoint center= [sender center];
CGPoint rootViewPoint = [[sender superview] convertPoint:center toView:_tableView1];
NSIndexPath *indexPath = [_tableView1 indexPathForRowAtPoint:rootViewPoint];
NSLog(#"%#",indexPath);
SWIFT 2 UPDATE
Here's how to find out which button was tapped
#IBAction func yourButton(sender: AnyObject) {
var position: CGPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(position)
let cell: UITableViewCell = tableView.cellForRowAtIndexPath(indexPath!)! as
UITableViewCell
print(indexPath?.row)
print("Tap tap tap tap")
}