I'm using TPKeyboardAvoiding in my app to hide move text fields when the keyboard is showing, but I'm getting an exception when I try to end editing the text field. It's coming from this method in TPKeyboardAvoiding:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView* view =[self TPKeyboardAvoiding_findFirstResponderBeneathView:self];
NSLog(#"%#",[view description]);
[view resignFirstResponder]; //this line gives the exception
[super touchesEnded:touches withEvent:event];
}
I'm a bit confused here. Shouldn't all UIViews respond to resignFirstResponder? Thanks for the help.
Full error:
2014-03-25 17:40:39.919 Rysk[5553:70b] -[MenuViewController textFieldDidBeginEditing:]: unrecognized selector sent to instance 0xb63c820
Not sure if you have called [yourTextField resignFirstResponder] as well. So it is possible that the UITextField (in the code you have provided) is not the FirstResponder at that point. I would suggest to adjust your code like this:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView* view =[self TPKeyboardAvoiding_findFirstResponderBeneathView:self];
if([view conformsToProtocol:#protocol(UITextFieldDelegate)] || [view conformsToProtocol:#protocol(UITextViewDelegate)]) &&
[view isFirstResponder] && [view canResignFirstResponder])
{
[view resignFirstResponder];
}
[super touchesEnded:touches withEvent:event];
}
Also, if you using PODs please make sure you are using a latest version, because the one i am using has something like this in this event:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[[self TPKeyboardAvoiding_findFirstResponderBeneathView:self] resignFirstResponder];
[super touchesEnded:touches withEvent:event];
}
Hope it helps!
Update your code as follows:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIView* view =[self TPKeyboardAvoiding_findFirstResponderBeneathView:self];
NSLog(#"%#",[view description]);
if([view isKindOfClass:[UITextField class]]) {
UITextField *myTextField = (UITextField *)view;
[myTextField resignFirstResponder];
}
[super touchesEnded:touches withEvent:event];
}
Try this simple method....
- (void)viewDidLoad
{
[super viewDidLoad];
//--Gesture to resign keyborad on touch outside
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard)];
tap.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:tap];
}
//-- Resign keyboard on touch UIView
-(void)dismissKeyboard
{
self.view.frame = CGRectMake(self.view.frame.origin.x, 0, self.view.frame.size.width, self.view.frame.size.height);
[self.view endEditing:YES];
}
I solved the problem by having my view controller than contains the TPKeyboardAvoidingScrollView implement the UITextFieldDelegate protocol. Most importantly, these two methods:
- (void)textFieldDidBeginEditing:(UITextField *)textField{}
- (void)textFieldDidEndEditing:(UITextField *)textField{}
Related
This question already has answers here:
iOS - Dismiss keyboard when touching outside of UITextField
(38 answers)
Closed 6 years ago.
I am using this method but keyboard isn't hiding.
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[_myTextView resignFirstResponder];
}
How can I dismiss the keyboard on simply touching outside the keyboard. Would be great if the solution is for both objective c and swift. Thanks
I have a query in objective C for textView inside scrollView. Please resolve. I've searched but have found no discrete solution.
try this
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
[self.yourScrollviewName endEditing:YES];
}
or use like
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if (![[touch view] isKindOfClass:[UITextView class]]) {
[self.view endEditing:YES];
[self.yourScrollviewName endEditing:YES];
}
[super touchesBegan:touches withEvent:event];
}
the above code not work try this
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *resignView = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleSingleTapforResign:)];
resignView.cancelsTouchesInView = NO;
[self.view addGestureRecognizer: resignView];
}
Dismiss what ever is currently editing:
- (void) handleSingleTapforResign:(UITapGestureRecognizer *) sender
{
[self.view endEditing:YES];
[self.yourScrollviewName endEditing:YES];
}
I see a lot of question about it on web, and particularly on StackOverflow.
I test many given answers but in my case, nothing works.
My class implements the protocol UIGestureRecognizerDelegate:
#interface CDMapViewController : CDViewController <UIGestureRecognizerDelegate>
The following method is wrote from xcode autocompletion in #implentation of my class
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
NSLog(#"not called");
return NO;
}
I have correctly init the UIGestureRecognizer in first method which correctly call the second, third and fourth methods:
- (void)initGestureOnMap {
UIGestureRecognizer *gestureRecognizer = [[UIGestureRecognizer alloc] init];
gestureRecognizer.delegate = self;
[self.view addGestureRecognizer:gestureRecognizer];
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
gesture_dragging = NO;
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
gesture_dragging = YES;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
if (gesture_dragging || [touches count] != 1) return;
/* bla bla bla */
}
... It doesn't log – not called ... why ?
You need to call super implementations for the touches methods.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
gesture_dragging = NO;
}
... and so on.
These methods need to be implemented on your view, and not your view controller.
Pick what kind of gesture you want. By itself, UIGestureRecognizer doesn't do much, so pick one like UITapGestureRecognizer. Next, implement your gesture recognizer with the designated initializer.
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(myMethod:)];
Finally, implement myMethod:.
-(void)myMethod:(UITapGestureRecognizer *)recognizer
{
// Whatever this does.
}
I am trying to create a button subclass which colours a button (when it is selected) dependent on whether it is the correct answer to a multiple choice question.
I am doing this like so:
In my UIViewController:
- (IBAction)answerQuestion:(id)sender {
QGPrettyButton *answerButton;
if ([sender isKindOfClass:[UIButton class]]) {
answerButton = (QGPrettyButton *)sender;
answerButton.isCorrectAnswer = [self.quizGame checkAnswer:self.currentQuestion answer:answerButton.titleLabel.text];
[self performSelector:#selector(removeQuestion) withObject:nil afterDelay:TIME_TO_SHOW_CORRECT_ANSWER];
}
}
In the setter method for isCorrectAnswer:
- (void)setIsCorrectAnswer:(BOOL)isCorrectAnswer
{
self.selected = YES;
[self setNeedsDisplay];
[self performSelector:#selector(returnToUnselected) withObject:nil afterDelay:TIME_TO_SHOW_CORRECT_ANSWER];
_isCorrectAnswer = isCorrectAnswer;
}
- (void)returnToUnselected
{
self.selected = NO;
[self setNeedsDisplay];
}
And in my draw method for QGPrettyButton:
CGFloat hue = UNSELECTED_HUE;
CGFloat saturation = UNSELECTED_SATURATION;
if (self.state == UIControlStateHighlighted) {
hue = HOVERED_HUE;
saturation = HOVERED_SATURATION;
}
if (self.state == UIControlStateSelected) {
hue = self.isCorrectAnswer ? CORRECT_HUE : INCORRECT_HUE;
}
Overriding touch :
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self setNeedsDisplay];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
[self setNeedsDisplay];
[self performSelector:#selector(hesitateUpdate) withObject:nil afterDelay:0.1];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self setNeedsDisplay];
[self performSelector:#selector(hesitateUpdate) withObject:nil afterDelay:0.1];
}
This is all working fine if you mouse down on the button and release again, but if you tap it really quickly the IBAction is called but the colour of the button is not set and it just flashes quickly white then back to the unselected colour...
You can try the highlighted property of a UIView.
If you want it to be highlighted do:
self.highlighted = YES;
and vice versa.
You might try moving [self setNeedsDisplay] out of your touchesEnded:withEvent: and touchesCancelled:withEvent: methods and into your hesitateUpdate method.
I have been trying to figure out how to forward the touches from a UIScrollView to its superview. The reason is for sort of a cursor to follow the touch. Anyway, the scrollview is populated with UIButtons. My code for forwarding the touch is to use delegation in a scrollview subclass:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateBegan:)]) {
[[self tfDelegate]tfDelegateBegan:[touches anyObject]];
}
NSLog(#"touches began");
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateMoved:)]) {
[[self tfDelegate]tfDelegateMoved:[touches anyObject]];
}
NSLog(#"touches moved");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateEnded:)]) {
[[self tfDelegate]tfDelegateEnded:[touches anyObject]];
}
NSLog(#"touches ended");
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesCancelled:touches withEvent:event];
if ([[self tfDelegate]respondsToSelector:#selector(tfDelegateCancelled:)]) {
[[self tfDelegate]tfDelegateCancelled:[touches anyObject]];
}
NSLog(#"touches cancelled");
}
However, I have learned that UIScrollviews operate via UIGestureRecognizers, so these methods aren't even called by default. I realize that the gesture recognizers are exposed in iOS 5 but I need to support 4.0 as well. I did this instead:
NSArray *a = [[theScrollview gestureRecognizers]retain];
for (UIGestureRecognizer *rec in a) {
if ([rec isKindOfClass:[UIPanGestureRecognizer class]]) {
NSLog(#"this is the pan gesture");
rec.cancelsTouchesInView = NO;
}
}
This allows the gesture to work and the touches methods to be called simultaneously. The issue is, now if you try to scroll while touching a button, the button can be pressed while scrolling. Normally, the scroll cancels the button and the only time a button can be pressed is if the scrollview is not scrolling.
This is the desired functionality. Any suggestions on how I might achieve this?
Maybe try controlling the button action using a flag that would prevent the event from firing if the scroll view is scrolling.
BOOL isScrolling = NO;
- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
isScrolling = YES;
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
isScrolling = NO;
}
- (void) didTapButton {
if(isScrolling)
return;
//Do Button Stuff
}
How do we detect a tap & hold on a UITableViewCell?
In iOS 3.2 or later you can use UILongPressGestureRecognizer
Here's the code lifted straight from my app. You should add these methods (and a boolean _cancelTouches member) to a class you derive from UITableViewCell.
-(void) tapNHoldFired {
self->_cancelTouches = YES;
// DO WHATEVER YOU LIKE HERE!!!
}
-(void) cancelTapNHold {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(tapNHoldFired) object:nil];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self->_cancelTouches = NO;
[super touchesBegan:touches withEvent:event];
[self performSelector:#selector(tapNHoldFired) withObject:nil afterDelay:.7];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self cancelTapNHold];
if (self->_cancelTouches)
return;
[super touchesEnded:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[self cancelTapNHold];
[super touchesMoved:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self cancelTapNHold];
[super touchesCancelled:touches withEvent:event];
}
//Add gesture to a method where the view is being created. In this example long tap is added to tile (a subclass of UIView):
// Add long tap for the main tiles
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longTap:)];
[tile addGestureRecognizer:longPressGesture];
[longPressGesture release];
-(void) longTap:(UILongPressGestureRecognizer *)gestureRecognizer{
NSLog(#"gestureRecognizer= %#",gestureRecognizer);
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
NSLog(#"longTap began");
}
}
You should probably handle the UIControlTouchDown event and depending on what you mean by "hold", fire a NSTimer that will count an interval since you initiated the touch and invalidate upon firing or releasing the touch (UIControlTouchUpInside and UIControlTouchUpOutside events). When the timer fires, you have your "tap & hold" detected.