How can I dismiss the keyboard in iOS when the textfield does not have an IBOutlet to the view controller? My case is a UITableView with dynamic prototype cells. One of those cells contains a UITextField, however I cannot add an IBOutlet because outlets are not allowed in repeating content.
So how can I achieve to dismiss the keyboard when textfield does not have outlet?
Add any one method in your ViewController.m file :
Choice -1
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
Choice -2
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(resignFieds)];
[self.view addGestureRecognizer:tap];
- (void)resignFieds {
//choice - 1 , check the all subviews and resign textfield
for (UIView * txt in self.view.subviews){
if ([txt isKindOfClass:[UITextField class]] && [txt isFirstResponder]) {
[txt resignFirstResponder];
}
else
{
[self.view endEditing:YES];
}
}
//choice 2 , no need to check any subviews ,
[self.view endEditing:YES];
Note : use any one choice
}
You can use this:
[view endEditing:YES];
try these..
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(taped:)];
[tap setNumberOfTapsRequired:1];
[self.view addGestureRecognizer:tap];
-(void)taped:(UITapGestureRecognizer*)gesture
{
[self.view endEditing:YES];
}
OR
you can do these also..
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if (![[touch view] isKindOfClass:[UITextField class]])
{
[self.view endEditing:YES];
}
[super touchesBegan:touches withEvent:event];
}
i hope it helps..
Try this :-
In Swift:-
UIApplication.sharedApplication().sendAction(Selector("resignFirstResponder"), to: nil, from: nil, forEvent: nil)
In Objective-C:-
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) to:nil from:nil forEvent:nil];
Explanation:-
We can send an action to the first responder by calling sendAction:to:from:forEvent on the UIApplication singleton and passing nil as the target and in your case your first responder is your textfield.
I assume that you’ve created custom dynamic prototype cell which have UITextField in it.
No need to have IBOutlet of UITextField in your ViewController class where you have added UITableView.
Create Custom TableViewCell Class, then create IBOutlet of UITextField in Custom TableViewCell Class.
#interface SimpleTableViewCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UITextField *simpleTxtField;
#end
Set TableViewCell Class to Custom TableViewCell class say “SimpleTableViewCell”. by selecting TableViewCell and selecting Identity Inspector icon in Right side pane in Xcode.
in your ViewController class confirm to UITextField Delegate. like this.
#interface ViewController () <UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITableView *simpleTableView;
#end
implement UITextField delegate Method.
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
then in tableView:cellForRowAtIndexPath method of UITableView Datasource set your Custom TableViewCell’s (SimpleTableViewCell) TextField Delegate to self.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SimpleTableViewCell *cell = [self.simpleTableView dequeueReusableCellWithIdentifier:#"simpleCell" forIndexPath:indexPath];
cell.simpleTxtField.delegate = self;
return cell;
}
and whenever you select TextField of Cell a Keyboard will display and when you click return key a TextField Delegate method will get call and Keyboard will get dismiss.
Hope this will help.
Related
I have a tableview with 8 custom cells. in the 8th cell I added a scrollView with paging enabled so I can show page 1 and page 2 (or 3, 4... 10) without have a very high cell.
The problem is with the scrollView I can't use didSelectRowAtIndexPath because the cell is behind the scrollView so I'm trying to detect scrollView tap (not swipe).
I played with touchesBegan and touchesEnded but they are never called (I know touches work with UIView only, but maybe.....)
Any help is very appreciated.
Thanks,
Max
There is a trick Apple recommends to use in this case, in theirs WWDC 2014 session "Advanced scrollviews" (See Demo starting from 8:10):
[cell.contentView addSubview:_scrollView];
[_scrollView setUserInteractionEnabled:NO];
[cell.contentView addGestureRecognizer:_scrollView.panGestureRecognizer];
That's all what needs to be done, no need to override touchesBegan:, touchesMoved: and others.
I used solution based on overriding of touchesBegan:, touchesMoved:, touchesEnded: and touchesCancelled: previously, but sometimes it caused a weird behaviour: when select a certain cell, method -tableView:didSelectRowAtIndexPath: was called for cell with different indexPath.
Solution from Apple has no side effects so far and looks more elegant.
There is also an elegant resolution:
Create a SubClass from UIScrollView and override the following methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self superview]touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self superview]touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self superview]touchesCancelled:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[self superview]touchesEnded:touches withEvent:event];
}
Passing every touch to the superview of the scroll view and then the didSelectRowAtIndexPath will be called.
Solved subclassing both uitableviewcell and uiscrollview.
It worked for my needs. Hope it can help.
Max
myScrollView.h
#import <UIKit/UIKit.h>
#interface myScrollView : UIScrollView {
}
#end
myScrollView.m
#import "myScrollView.h"
#implementation myScrollView
- (id)initWithFrame:(CGRect)frame {
return [super initWithFrame:frame];
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
NSLog(#"touch scroll");
// If not dragging, send event to next responder
if (!self.dragging)
[self.nextResponder touchesEnded: touches withEvent:event];
else
[super touchesEnded: touches withEvent: event];
}
myCell.h
#import <UIKit/UIKit.h>
#interface myCell : UITableViewCell {
}
#end
myCell.m
#import "myCell.h"
#implementation myCell
- (id)initWithFrame:(CGRect)frame {
return [super initWithFrame:frame];
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
NSLog(#"touch cell");
// If not dragging, send event to next responder
[super touchesEnded: touches withEvent: event];
}
RootViewController.h
#import <UIKit/UIKit.h>
#class myCell;
#class myScrollView;
#interface RootViewController : UITableViewController {
myCell *cell;
myScrollView *scrollView;
}
#end
RootViewController.m
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
// my custom cell
cell = [[myCell alloc] init];
if (cell == nil) {
cell = [[[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// the custom scroll view
scrollView = [[myScrollView alloc] initWithFrame:cell.frame];
scrollView.contentSize = CGSizeMake(640, 40);
[cell.contentView addSubview:scrollView];
//something to add in scrollView
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 150, 20)];
label.text = #"some text";
[scrollView addSubview:label];
// Configure the cell.
return cell;
}
The selected answer is correct, but I updated the code based on a bug I was getting.
In the subclassed scroll view add the following code.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.dragging) {
[super touchesMoved:touches withEvent:event];
} else {
if ([self.delegate isKindOfClass:[UITableViewCell class]]) {
[(UITableViewCell *)self.delegate touchesCancelled:touches withEvent:event];
}
[self.superview touchesMoved:touches withEvent:event];
}
}
If your self.delegate is not the UITableViewCell, than replace that property with a property to your cell.
The cell needs to retrieve the cancel touch event during movement to prevent the undesired results. It can be easily reproducible as follows.
Highlight the cell (assuming the scroll view is over the whole cell, if not highlight the scroll view)
While the cell is highlighted, drag the table view
Select any other cell and now the previously highlighted cell will retrieve the didSelectCell state
Another point to mention is that order matters! If the self.delegate is not called before the self.superview then the highlighted state wont happen.
I found the simplest solution for my needs:
subclass UIScrollView touchesEnded method and post a notification.
In the UITableview add an observer in viewdidAppear (remove it in viewdiddisappear) to call a function that call tableview didSelectRowForIndexPath.
Something like this (swift version)
// myScrollView.swift
import UIKit
class myScrollView: UIScrollView {
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
NSNotificationCenter.defaultCenter().postNotificationName("selectTVRow", object: nil)
}
}
In your tableView:
// ItemsList.swift
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "selectFourthRow", name: "selectTVRow", object: nil)
}
override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "selectfourthrow", object: nil)
}
func selectFourthRow() {
let rowToSelect:NSIndexPath = NSIndexPath(forRow: 4, inSection: 0);
self.tableView(self.tableView, didSelectRowAtIndexPath: rowToSelect);
}
/*
.... rest of your tableview Datasource and Delegate methods...
numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath
*/
In my app I have UITableView cells with multiple buttons. 4 UIButtons per cell. For each UIButton I want to add a UILongPressGestureRecognizer. Code below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
FriendViewCell *cell = (FriendViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
for(int i =0; i<4; i++) {
UIButton *button = cell.buttons[i];
UILabel *label = cell.labels[i];
[button setTag:(int)indexPath.row*4+i];
[button addTarget:self action:#selector(friendTapped:) forControlEvents:UIControlEventTouchUpInside];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPressFriend:)];
[button addGestureRecognizer:longPress];
}
}
I just realised though that if a cell is reused then I am adding a gesture multiple times per button. Is there a way to detect if the cell created is reused or new? I don't think I can move the code to the FriendViewCell class because my gesture target friendTapped: is in my UITableViewController. Any pointers will be greatly appreciated! thanks
A cleaner way would be to create a custom class for your Button. This class would have an UILongPressGestureRecognizer created at initialization and a delegate (your controller) that will be called when the gesture is triggered.
.h
#class MyLongPressedButton
#protocol MyLongPressedButtonDelegate
- (void)buttonIsLongPressed:(MyLongPressedButton *)button;
#end
#interface MyLongPressedButton
#property (nonatomic, weak) id<MyLongPressedButtonDelegate > delegate
#end
.m
-(id)init {
if (self = [super init]) {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:#selector(longPress:)];
[self addGestureRecognizer:longPress];
}
return self;
}
-(void)longPress:(id)sender {
[_delegate buttonIsLongPressed:self]
}
First of all, there are fast ways to solve this - you can check the existing gesture recognizers on the button whether there is a long press gesture recognizer.
A slightly better solution is to define a property on the cell, e.g.
#property (nonatomic, assign) BOOL recognizerAdded
However, the best solution is to do this inside the cell class
#implementation FriendViewCell
- (void)awakeFromNib {
//add the recognizers
}
#end
Note that you your table delegate shouldn't care about the structure of your cell, it's the cell's responsibility to setup itself properly.
Of course, you will need a delegate on the cell to notify you about the action but it will be a cleaner solution.
I would recommend placing the code inside your cell and creating a delegate method that will return a reference to the cell.
E.g.
#protocol myAwesomebuttonCellDelegate <NSObject>
- (void)myAwesomeCell:(MyAwesomeCell *)cell buttonPressed:(UIButton *)btn;
#end
Then in your view controller you can use:
NSIndexPath *index = [tblView indexPathForCell:cell];
to get the row / section
I'm using UITapGestureRecognizer to end editing (because that's the workaround I found useful to end editing on input keyboards that have no other way to do so, like the Decimal Pad). The problem is that inside that viewController I have a tableView (the UITableViewDataSource and UITableViewDelegate of that tableView are set to the viewController) and the method didSelectRowAtIndexPath is not being triggered.
Code:
viewDidLoad {
...
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
[self.view addGestureRecognizer:tapRecognizer];
...
}
- (void)tap:(UIGestureRecognizer*)gr {
[self.view endEditing:YES];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Help!");
}
I know the UITapGestureRecognizer is catching the selection, because if I comment out as following:
viewDidLoad {
...
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap:)];
//[self.view addGestureRecognizer:tapRecognizer];
...
}
now the method the didSelectRowAtIndexPath finally triggers out.
I need help with some good practices on how to workaround the "endEditing" or how to forward the tap gesture to the tableView so that the didSelectRowAtIndexPath triggers out.
Thanks!
I assume that you haven't a tableView that takes all the screen, otherwise you can close the keyboard in didSelectRowAtIndexPath.
Any way, you can do your stuff and the pass the event to the next responder (that is kind of class UITableViewCell if there is, so if user tap on the tableView):
- (void)tap:(UIGestureRecognizer*)gr {
[self.view endEditing:YES];
UIResponder *responder = self;
while (responder.nextResponder != nil){
responder = responder.nextResponder;
if ([responder isKindOfClass:[UITableViewCell class]]) {
break;
}
}
[responder touchesBegan:touches withEvent:event];
}
I am trying to figure out how to dismiss the keyboard and trigger a method when the user taps outside of a UITextField
in TableViewCell.m:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
[self.delegate cellDidBeginEditing:self];
}
in ViewController.m:
-(void)cellDidBeginEditing:(TableViewCell *)editingCell
{
_editingOffset = _tableView.scrollView.contentOffset.y - editingCell.frame.origin.y;
for (TableViewCell *cell in [_tableView visibleCells]) {
[UIView animateWithDuration:0.3
animations:^{
cell.frame = CGRectOffset(cell.frame, 0, _editingOffset);
if (cell != editingCell) {
cell.alpha = 0.25;
}
}];
}
}
the cellDidBeginEditing: method displaces the cell to the top and shades the other cells grayish.
I have another method, cellDidEndEditing: which does the opposite of this, and the code is not really needed.
As of now, selecting, for example, "cell2" when editing "cell1" just triggers cellDidBeginEditing for "cell2"
I want the keyboard to dismiss and cellDidEndEditing to trigger when I click outside of "cell1"
Try implementing and calling these functions:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesBegan:withEvent");
[self.view endEditing:YES];
[super touchesBegan:touches withEvent:event];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self.firstTextField resignFirstResponder];
[self.secondTextField resignFirstResponder];
return YES;
}
This should make it so that when you touch any where outside of the two textFields, the keyboard automatically dismisses.
I am new in iOS development. I want to hide the keyboard when tapping outside of a UITextView.
My TextView is in a cell from an UITableView. The problem is that I have a Toolbar at the top and my buttons doesn't react anymore. I implemented the method "shouldReceiveTouch" but my test is not correct i think. Any ideas? Thank you and sorry for my bad english..
In my ViewDidLoad:
tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
tap.delegate = self;
[self.view addGestureRecognizer:tap];
note: tap is an UITapGestureRecognizer property.
Implemented methods:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIBarButtonItem class]]) {
return NO;
}
return YES;
}
-(void)dismissKeyboard {
[tview resignFirstResponder];
}
UIBarButtonItem is not a subclass of UIView, hence the shouldReceiveTouch still return YES.
Try to exclude the whole UIToolbar or just add the tap gesture recognizer in the UITableViewCell when you initialize the cell in cellForRowAtIndexPath.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isKindOfClass:[UIToolbar class]]) {
return NO;
}
return YES;
}
You should add your gesture to table view.
tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
tap.delegate = self;
[tblView addGestureRecognizer:tap];
use didScroll method of UIScrollView delegate to resign keyboard.TableView is also subclass of UIScrollView so it should work.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[tview resignFirstResponder];
}
or use this one
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[tview resignFirstResponder];
}
If you still want to use gesture then add gesture to UIView or self.view or superView of tableView
instead of adding it to tableView
Try following code :----
Keep you code as it is and add this method
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[tview resignFirstResponder];
}
in viewDidLoad set self.view.userInteractionEnabled = yes;
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if ([touch view] == tview) {
[tview resignFirstResponder];
}
}