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.
Related
I have added a textfield in oe of the cell in my tableview.
Now I want to compare if the touched object is textfield or not. For that I am using -
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
UITouch *touch = [[event allTouches] anyObject];
}
method. Here I have to get the view touched in UITableview cell is textfield.
How can I get that? I am struct at this. Please help.
Use TextField Delegate method :
-(void)textFieldDidBeginEditing:(UITextField *)textField
I think the best way to achieve this would be:
Create a UITableViewCell subclass with a UITextField as a subview. Define it as an IBOutlet and hook it up via interface builder.
Create a delegate protocol to notify a delegate (in your case it could be the same class handling the UITableViewDataSource) of this event.
Declare the UITableViewCell subclass as a UITextFieldDelegate and hook it up to the UITextField you've created. via IB
Implement
-(void)textFieldDidBeginEditing:(UITextField *)textField {
[self.delegate textFieldWasTapped];
}
Now you'll be notified in the main controller of a textFieldWasTapped event. There's no need to check if it indeed came from a UITextField object because only instances if this type will be able to trigger this call.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
UITouch *touch = [[event allTouches] anyObject];
if([[touch view] isKindOfClass:[UITextField class]])
{
UITextField *txtField = (UITextField *) [touch view];
//UITextField object
}
}
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];
}
}
I have a UIControl which implements the touches began method like so:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
//More code goes here
This subclass of UIControl is instantiated in a view controller, it is then added as a subview to that view controller. I have a breakpoint at the touches began method of the UIControl, and the method never gets called. I've been doing some reading and it seems that the View Controller has some logic that decides whether to pass on touch events to its subviews. The strange thing is that I have a different subclass of UIControl in the same view controller, and the touch events get passed down to it when the user touches it!
Here is the full code:
.h
#import <UIKit/UIKit.h>
#interface CustomSegment : UIView
#property (nonatomic, strong) UIImageView *bgImageView;
#property (nonatomic, assign) NSInteger segments;
#property (nonatomic, strong) NSArray *touchDownImages;
#property (nonatomic, readonly, assign) NSInteger selectedIndex;
#property (nonatomic, weak) id delegate;
- (id)initWithPoint:(CGPoint)point numberOfSegments:(NSInteger)_segments andTouchDownImages:(NSArray *)_touchDownImages;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
#end
.m
#import "CustomSegment.h"
#implementation CustomSegment
#synthesize bgImageView, segments, touchDownImages, selectedIndex, delegate;
- (id)initWithPoint:(CGPoint)point
numberOfSegments:(NSInteger)_segments
andTouchDownImages:(NSArray *)_touchDownImages
{
self = [super initWithFrame:CGRectMake(point.x, point.y, [[_touchDownImages objectAtIndex:0] size].width, [[touchDownImages objectAtIndex:0] size].height)];
if (self)
{
touchDownImages = _touchDownImages;
segments = _segments;
bgImageView = [[UIImageView alloc] initWithImage:[touchDownImages objectAtIndex:0]];
[self addSubview:bgImageView];
}
return self;
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
float widthOfSegment = [self frame].size.width / segments;
float bottomPoint = 0;
float topPoint = widthOfSegment;
for (int i = 0; i < segments; i++)
{
if ([touch locationInView:self].x > bottomPoint && [touch locationInView:self].x < topPoint)
{
[bgImageView setImage:[touchDownImages objectAtIndex:i]];
selectedIndex = i;
return;
}
else
{
bottomPoint = topPoint;
topPoint += topPoint;
}
}
}
#end
tl;dr Set all subviews of the UIControl to setUserInteractionEnabled:NO. UIImageViews have it set to NO by default.
Original Post
One thing I found recently is that it helps if the top-most subview of the UIControl has setUserInteractionEnabled:NO. I arrived at this because I had a UIControl subclass with a UIImageView as it's only subview and it worked fine. UIImageView has userInteractionEnabled set to NO by default.
I also had another UIControl with a UIView as it's top most subview (technically the same UIControl in a different state). I believe UIView defaults to userInteractionEnabled == YES, which precluded the events being handled by the UIControl. Settings the UIView's userInteractionEnabled to NO solved my issue.
I don't know if it's the same issue here, but maybe that will help?
--
Edit: When I say topmost view... probably set all subviews of the UIControl to setUserInteractionEnabled:NO
Check frames of all parent views. The rule is that if sub-view (or its part) of the view is outside the view bounds, it doesn't receive touch events.
Xcode 12 and Latter.
As mention in the accepted answer, make sure all the subviews of the
UIControl view get "User Interaction Enabled" unchecked.
Select your
UIControl view and switch to the "Connection Inspector" and make
sure it has been connected to "Touch Up Inside" event. Sometimes
Xcode uses "Value Changed" event so make sure to change to "Touch
Up Inside" event
It is possible that another view is covering your UIControl, and preventing it from receiving the touch events. Another possibility is that userInteractionEnabled is set to NO somewhere by mistake.
EDIT: I see that you added more code above. Did you verify that your view's frame has a width and height greater than zero? It looks to me like you are calling "size" directly on an object from NSArray (which is 'id'). I don't know how you are doing this without a cast (perhaps the parenthesis didn't come through above?) but if you are somehow pulling it off I wouldn't be surprised if it was an invalid value.
First, why do you call [super touchesBegan:touches withEvent:event];? This call is forwarding the event to the next responder. Usually you do it only when you don't want to handle the event. Are you sure you know what are you doing there?
An idea why it doesn't work - can it be that you have a gesture recognizer which handles the event first?
Anyway, do you really need to override touchesBegan? UIControl is made to track the touch events by itself and call your handlers in response. And the UIControl docs say HOW to subclass it.
Subclassing Notes
You may want to extend a UIControl subclass for either of two reasons:
To observe or modify the dispatch of action messages to targets for particular events
To do this, override sendAction:to:forEvent:, evaluate the passed-in selector, target object, or UIControlEvents bit mask, and proceed as required.
To provide custom tracking behavior (for example, to change the highlight appearance)
To do this, override one or all of the following methods: beginTrackingWithTouch:withEvent:, continueTrackingWithTouch:withEvent:, endTrackingWithTouch:withEvent:.
Possibilities:
You might have forgot to set the delegate for the UIControl.
The other UIControl which receives the touch is obscuring/covering over the UIControl.
Fair enough. Recently re-investigated this (UIControl is very poorly documented) and realised that tracking touches is a replacement for touchesBegan/Ended, not an additional, so sorry about that. I'd reverse the vote but it wouldn't let me :(
You might need to override intrinsicContentSize inside your UIControl subclass.
override var intrinsicContentSize: CGSize {
return CGSize(width: 150, height: 100)
}
I don't quite understand how it fixes it or why, but it works. It doesn't even need to be the exact size as your control.
UIView instances are unable to respond to events so there is no way, up to your current configuration, to trigger your action method ?
Try to change the class of the view to be an instance of the UIControl class !
I have been using the ABTableViewCell to create fast scrolling cells. All is working great apart from I can't figure out how to add a UIButton to my custom cell.
When using ABTableViewCell you do all the drawing of the text/images yourself using drawAtPoint or drawInRect. However UIButton doesn't support any of these methods. You can't do addSubView as the whole point of using ABTableViewCell is that you only have one view.
Anyone had any idea of how to do this?
Thanks
I have a "share" button inside a ABTableViewCell sublcass.
static UIImage* imgShare=nil;
+ (void)initialize
{
if(self == [MyCustomCell class])
{
imgShare = [[UIImage imageNamed:#"share.png"] retain];
}
}
I use a yellow rect to debut the active area of the button.
In drawRect callback:
CGRect btnShareActiveAreaRect;
- (void)drawContentView:(CGRect)r
{
[[UIColor yellowColor] set];
btnShareActiveAreaRect = CGRectMake(10,10,65,45);
CGContextFillRect(context, btnShareActiveAreaRect);
CGRect shareRect = CGRectMake(15,20,25,19);
[imgShare drawInRect:shareRect];
}
Handle the touch:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch =[touches anyObject];
CGPoint startPoint =[touch locationInView:self.contentView];
if(CGRectContainsPoint(btnShareActiveAreaRect,startPoint))
{
[self shareButton_Click];
}
else
[super touchesBegan:touches withEvent:event];
}
One alternative to Loren Brichter's ABTableViewCell is setting the -shouldRasterize: flag on UITableViewCell's backing CALayer to YES. -shouldRasterize: will draw your cell off-screen and cache it. This is a good approach if your cells are generally pretty simple and homogenous. To access these layer properties, remember that you'll have to link against the QuartzCore framework.
Keep in mind that this approach will generally not give you the same results if you are animating in your cell as -shouldRasterize: will attempt to draw and cache for every frame of the animation.
Make a subclass of it should work. Do the following :
When defining the cell, do this :
MyFunkyCellClass *cell = (MyFunkyCellClass*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
MyFunkyCellClass.h:
#import <UIKit/UIKit.h>
#interface MyFunkyCellClass : UITableViewCell {
UIButton *fancyButton;
}
#property (nonatomic, retain) UIButton *fancyButton;
#end
in MyFunkyCellClass.h:
#synthesize fancyButton;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
fancyButton = [[UIButton alloc] initWithFrame:CGRectMake(113, 2, 113, 31)];
[self addSubview:fancyButton];
}
return self;
}
This is roughly how it should be done :]
UITableViewCell is a subclass of UIResponder, so you could always consider handling touches manually in a specific area of the cell's bounds without the use a subview (flat views being one of the key ABTableViewCell approaches for fast scrolling).
Then, you could set up your own delegation or target/action mechanism to have your cell inform other areas of the application about interaction, or just draw the button area of the cell differently.
Also, if you override UIResponder methods and do not handle the input, be sure to call super so the UITableView will recognized row selection, etc.
I have a UITableViewController and want to detect touches.
Basically, I want the user to be able to hold down a touch for 3 seconds, when they let go I want to pop up a small view with a few options for the table.
I've tried this...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.lastTouch = [event timestamp];
NSLog(#"TLC.touchesBegan:touchBeginEndInterval %f", self.lastTouch);
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSTimeInterval touchBeginEndInterval = [event timestamp] - self.lastTouch
NSLog(#"TLC.touchesEnded:touchBeginEndInterval %f %f", self.lastTouch, touchBeginEndInterval);
}
And it isn't picking up my touches at all...
Any ideas about the best way to implement this?
TableViews have sort of a "touch" delegate method. Implement this method for your tableView delegate:
// What happens when a row is touched
- (void)tableView:(UITableView *)table didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}
You shouldn't make them press for 3 seconds... just use the standard.
Assuming that code comes from your UITableViewController subclass ... UIViewControllers don't receive methods like touchesBegan:withEvent. Only UIResponders like UIView an its subclasses like UITableView do. So you are trying to get the touches in the wrong place.
What are you trying to accomplish? Do you want to respond to touches in the UITableView or in a UITableViewCell? In case of the latter, you can deal with the touches in your custom cell implementation.
Now it's easy to accomplish with UILongPressGestureRecognizer:
-(void)viewDidLoad{
[super viewDidLoad];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandlerMethod:)];
[self.tableView addGestureRecognizer:longPressRecognizer];
}
-(void)longPressHandlerMethod:(UIGestureRecognizer *)gestureRecognizer{
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:[gestureRecognizer locationInView:self.tableView]];
...
}