I have an UIView which has an UITapGestureRecognizer attached to it that I use to hide the keyboard when the user taps outside the UITextFields. Now, I also have some labels that when tapped show an UIPickerView. The labels use an UITapGestureRecognizer as well. The problem is that the events seem to canibalise themselves.
Is it possible to execute both event handlers when tapping on my labels?
Thank you.
UITapGestureRecognizer* tapForUnit = [[UITapGestureRecognizer alloc] initWithTarget:self.fridgeItemUnit action:#selector(onTap)];
[self.fridgeItemUnit addGestureRecognizer:tapForUnit];
The above code is for one of the labels. I have removed the code for the view because my labels would stop working, but it's exactly the same, only thing different is that it is attached to self.view and the function that is executed is this one:
-(void)dismissKeyboard:(UIGestureRecognizer*)gesture {
[self.fridgeItemName resignFirstResponder];
[self.fridgeItemQuantity resignFirstResponder];
}
I would implement the following method from UIGestureRecognizerDelegate:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
I attach an example:
The only thing I did on the XIB was to enabled the user interaction. An here is the .m of the UIViewController:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_viewRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(viewTap:)];
[_viewRecognizer setDelegate:self];
_labelRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(labelTap:)];
[_labelRecognizer setDelegate:self];
[self.view addGestureRecognizer:_viewRecognizer];
[self.label addGestureRecognizer:_labelRecognizer];
}
- (void)viewDidUnload {
[super viewDidUnload];
[_viewRecognizer release]; _viewRecognizer = nil;
[_labelRecognizer release]; _labelRecognizer = nil;
self.label = nil;
}
- (void)dealloc {
[_viewRecognizer release];
[_labelRecognizer release];
self.label = nil;
[super dealloc];
}
- (void)labelTap:(UIGestureRecognizer *)recognizer {
NSLog(#"labelTap");
}
- (void)viewTap:(UIGestureRecognizer *)recognizer {
NSLog(#"viewTap");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
NSLog(#"shouldRecognizeSimultaneouslyWithGestureRecognizer");
return YES;
}
Then when tapping on the label I get the following log:
shouldRecognizeSimultaneouslyWithGestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer
labelTap
viewTap
And when tapping on the view:
viewTap
Related
I add the gesture to view as following steps:
#interface iCarousel () <UIGestureRecognizerDelegate>
{
UIPanGestureRecognizer * mPanGesture;
UITapGestureRecognizer * mTapGesture;
}
#end
#implementation iCarousel
- (void)setUp
{
_contentView = [[UIView alloc] initWithFrame:self.bounds];
_contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/
mPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(iCarouselDidPan:)];
mPanGesture.delegate = self;
[_contentView addGestureRecognizer:mPanGesture];
/
mTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(iCarouselDidTap:)];
mTapGesture.delegate = self;
[_contentView addGestureRecognizer:mTapGesture];
/
self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
self.isAccessibilityElement = YES;
[self addSubview:_contentView];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self setUp];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
[self setUp];
}
return self;
}
...
- (void)iCarouselDidTap:(UITapGestureRecognizer *)tapGesture
{
...
}
- (void)iCarouselDidPan:(UIPanGestureRecognizer *)panGesture
{
...
}
...
#end
Now, the method in this class
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gesture
return me YES
But, the actions iCarouselDidPan: and iCarouselDidTap: do not fire.
The information is printed in gestureRecognizerShouldBegin: method.
(lldb) po _contentView.gestureRecognizers
<__NSArrayI 0x1572bec0>(
<UIPanGestureRecognizer: 0x16dea750; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidPan:, target=<iCarousel 0x16dea1b0>)>>,
<UITapGestureRecognizer: 0x16e8f310; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidTap:, target=<iCarousel 0x16dea1b0>)>>
)
(lldb) po gesture
<UIPanGestureRecognizer: 0x16dea750; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidPan:, target=<iCarousel 0x16dea1b0>)>>
It's only happened on devices <4s 9.3.x , 5 10.3.1> at current moment.
Try implement the following methods of protocol UIGestureRecognizerDelegate inside your iCarousel:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceivePress:(UIPress *)press {
return YES;
}
Possible cases for not working gestures .
1 - You need to enable interaction of view.
[_contentView setUserInteractionEnabled:YES];
2 - Add this delegate in your class.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Declare UIGestureRecognizerDelegate in .h file
Gesture definition
-(UITapGestureRecognizer*)setTapGesture{
UITapGestureRecognizer *Tap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapWithGestureRecognizer:)];
Tap.delegate = self;
return Tap;
}
-(void)handleTapWithGestureRecognizer:(UITapGestureRecognizer *)TapGestureRecognizer{
NSLog(#"tap Gesture");
}
-(UIPanGestureRecognizer*)setpanGesture{
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
return panRecognizer;
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
[self.view bringSubviewToFront:recognizer.view];
CGPoint touchLocation = [recognizer locationInView:self.view];
self.YOURVIEW.center = touchLocation;
}
Call Gesture or add gesture to your view
[self.YOURVIEW addGestureRecognizer:[self setTapGesture]];
[self.YOURVIEW addGestureRecognizer:[self setpanGesture]];
self.YOURVIEW.userInteractionEnabled = YES;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Good Luck
Happy Coding.
Here is the code:
This section is written on **-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath**
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imageDispalyFullScreenTapped:)];
singleTap.numberOfTapsRequired = 1;
singleTap.delegate=self;
cell.imgDisplay.userInteractionEnabled = YES;
[cell.imgDisplay addGestureRecognizer:singleTap];
Next I have implemented the delegate, i.e. **UIGestureRecognizerDelegate**
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return YES;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Now implemented the selector when UIImageView getting tapped
-(void)imageDispalyFullScreenTapped:(UITapGestureRecognizer*)gesture
{
UIView *tappedView = gesture.view;
UITableViewCell *cell=[self getSuperViewOfType:[UITableViewCell class] FromView:tappedView];
NSIndexPath *indexPath=[tblChat indexPathForCell:cell];
Sock_ModelChat *objChat=[arrDisplay objectAtIndex:indexPath.row];
if ([objChat.strUrlLocal stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length>0) {
imgSelectedDisplay=[[Sock_DB sharedInstance] getImageForName:objChat.strUrlLocal];
ImageDisplayerViewController *imageDisplayer=[ImageDisplayerViewController sharedInstance];
imageDisplayer.delegate=self;
[self addChildViewController:imageDisplayer];
imageDisplayer.view.frame=CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds));
[self.view addSubview:imageDisplayer.view];
[imageDisplayer didMoveToParentViewController:self];
}
}
But the problem is whenever tapping on the border side or edge side of the image view, the tap on image view getting detected and the selector imageDispalyFullScreenTapped fired.But when tapping on the center or near the center, tap is not getting detected and so the related selctor also not firing.
I am working on XCode Version 8.0 (8A218a) and iOS 10.0.
May be UserInteraction is disabled in your case
then you can add gesture like :
let gesture:UITapGestureRecognizer = UITapGestureRecognizer.init(target:self, action:#selector(handleTap))
gesture.numberOfTapsRequired = 1
gesture.delegate = self
cell.imageView?.addGestureRecognizer(gesture)
then add below function in your viewcontroller
func handleTap(sender: UITapGestureRecognizer? = nil) {}
I believe I have an issue with UITapGestureRecognizer for dismissing keyboard when tapping in chatroom area preventing or blocking touch to the previewCancelButton. Here below are my relevant codes:
BaseTemplateVC.m
- (void)addDismissKeyboardGesture {
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissKeyboard:)];
tapGesture.cancelsTouchesInView = NO;
tapGesture.delegate = self;
self.view.tag = 111;
[self.view addGestureRecognizer:tapGesture];
}
- (void) dismissKeyboard:(id)sender {
UITapGestureRecognizer *gesture = sender;
UIView *view = gesture.view;
NSLog(#"%ld", (long)view.tag);
[self.view endEditing:YES];
}
ChatroomVC.m
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//Disallow recognition of tap gestures in the segmented control.
if (([touch.view isKindOfClass:[UIButton class]])) {
NSLog(#"noooooooo");
return NO;
}
return YES;
NSLog(#"yesssssss");
}
InputFunctionView.m
- (void)selectedSticker:(NSString *)stickerURLString {
/* Sticker preview subview */
stickerPreviewView = [[UIImageView alloc] initWithFrame:CGRectMake(0, -120, FrameWidth, 120)];
stickerPreviewView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5f];
stickerPreviewView.userInteractionEnabled = YES;
[self addSubview:stickerPreviewView];
[self bringSubviewToFront:stickerPreviewView];
/* Initialise previewCancelButton */
self.previewCancelButton = [UIButton buttonWithType:UIButtonTypeCustom];
self.previewCancelButton.frame = CGRectMake(Main_Screen_Width-30, SpaceForItems-120, 20, 20);
[self.previewCancelButton setBackgroundImage:[UIImage imageNamed:#"btn_previewcancel.png"] forState:UIControlStateNormal];
[self.previewCancelButton setBackgroundImage:[UIImage imageNamed:#"btn_previewcancel.png"] forState:UIControlStateHighlighted];
[self.previewCancelButton addTarget:self action:#selector(cancelStickerPreviewButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview: self.previewCancelButton];
}
/* Cancel sticker preview subview */
- (void)cancelStickerPreviewButtonPressed:(id)sender {
NSLog(#"cancel sticker preview");
[self.previewCancelButton removeFromSuperview];
[stickerPreviewView removeFromSuperview];
}
Now the previewCancelButton is correctly on right top corner of stickerPreviewView but unable to receive touch event to it. When I touch the button it shows "111" in the console and when I traced back I found BaseTemplateVC.m that contains addDismissKeyboardGesture method, so I believe this may cause the issue.
Anyone can guide me to some solutions. That'd be really appreciated. Thanks in advance.
Progress: I have modified gestureRecognizer method in ChatroomVC.m so now it can ignore tap gesture on the button but the problem remains action for the button doesn't get fired.
Just take a try with this, i guess it will work.
Use the shouldReceiveTouch delegate method of gesture, and return NO when the touch.view is button class.
So when it finds button It will discard the gesture and take button action.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Block the recognition of tap gestures in the button.
if (([touch.view isKindOfClass:[UIButton class]])) {
return NO;
}
return YES;
}
Here is the demo implementation :
I have taken the button on main view of view controller in the story board.
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapGestureClicked:)];
tapGesture.delegate = self;
[self.view addGestureRecognizer:tapGesture];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures in the segmented control.
if (([touch.view isKindOfClass:[UIButton class]])) {
return NO;
}
return YES;
}
- (IBAction)btnTestClicked:(UIButton *)sender {
NSLog(#"test button click");
}
- (void)tapGestureClicked:(UIGestureRecognizer *)recog
{
NSLog(#"tap gesture clicked");
}
Hope it helps.
Happy coding ...
I found a solution to this by using below code in GestureRecogniser delegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if ([touch.view isDescendantOfView:IFView.stickerPreviewView]) {
return NO;
}
return YES;
}
It specifies exactly what subview in this case IFView.stickerPreviewView is to return NO. Also in InputFunctionView, use this instead to add the subview:
[self.superview addSubview:_stickerPreviewView];
i am creating drawer
self.isShowMenuVC = NO;
_menuView = [MenuViewController viewController];
[self.menuView setDelegate:self];
[self addChildViewController:self.menuView];
[self.menuView.view setFrame:CGRectMake(-kMenuTableWidth, 0, kMenuTableWidth, self.view.frame.size.height)];
[self.view addSubview:self.menuView.view];
[self.menuView didMoveToParentViewController:self];
UITapGestureRecognizer *outsideTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.view addGestureRecognizer:outsideTap];
outsideTap.delegate = self;
and when button tap i just set frame of _menuView.view to behave like a drawer
what i want is to detect touch outside of drawer but i am not able to do it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view == self.menuView.view) {
NSLog(#"Touch Drawer");
} else {
NSLog(#"Touch Outside");
}
return YES;
}
but it is always show Touch Outside"
i am missing something but don't know what thanks in advance
Also try with 2 gesture but not working because one gesture in self.view so, when i tap in drawer method call 2 times.
for that i tried to disable one gesture, still calling two times
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == self.touchInDrawer) {
NSLog(#"Touch in drawer");
[self.touchOutSideDrawer setEnabled:NO];
} else {
NSLog(#"Outside");
[self hideMenuView];
}
return YES;
}
The UITapGestureRecognizer cannot detect the touch outside the view which it belongs to.
You need to create another UITapGestureRecognizer and add them to self.menuView.view.
Also you can make two #property for your gesture recognizers and check them inside method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == self.firstGesture) {
NSLog(#"Touch in first gesture");
} else {
NSLog(#"Touch in another gesture");
}
I have solve this issue with adding two gesture
#property (strong,nonatomic) UITapGestureRecognizer *touchInDrawer;
#property (strong,nonatomic) UITapGestureRecognizer *touchOutSideDrawer;
as per #Eugene Zaychenko's Answer but there is still problem because delegate method calling two times
also i can't [self.touchOutSideDrawer setEnabled:NO]; when touch in drawer tapped, because after that it will remove from the view and never executed again if [self.touchOutSideDrawer setEnabled:YES];
but most interesting thing is
_touchInDrawer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.menuView.view addGestureRecognizer:self.touchInDrawer];
self.touchInDrawer.delegate = self;
_touchOutSideDrawer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.view addGestureRecognizer:self.touchOutSideDrawer];
self.touchOutSideDrawer.delegate = self;
outsideTapped method calling only one time so i shifted my all code there and is working
- (void) outsideTapped:(UITapGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.touchOutSideDrawer) {
// [self.view removeGestureRecognizer:self.touchOutSideDrawer];
[self hideMenuView];
NSLog(#"Outside");
} else {
NSLog(#"Touch in drawer");
// [self.touchOutSideDrawer setEnabled:NO];
}
}
Facing trouble with UITableViewCell.I tried to get the data of selected UITableViewCell. Initially it is working good but after the UITapGestureRecognizer its making this trouble. The table i've used is assigned to the one subview even few buttons are not taking the action at first click. I am facing this trouble exactly after adding the UITapGestureRecognizer.
tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(didTapAnywhere:)];
[tapRecognizer setNumberOfTapsRequired:1];
[tapRecognizer setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:tapRecognizer];
[self.scrolling addGestureRecognizer:tapRecognizer];
[self.notesView addGestureRecognizer:tapRecognizer];
-(void)didTapAnywhere: (UITapGestureRecognizer*) recognizer {
[ageview removeFromSuperview];
[contiView removeFromSuperview];
[CountryTableview removeFromSuperview];
[notesView endEditing:YES];
[self.view endEditing:YES];
}
Your Tap Guesture is cancelling the touch guesture to your cell and your Buttons, make sure that the view to which you are assigning a tap guesture does not overlap the buttons which would result in cancellation of touch events
Did you implement delegate method of UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
UIView *view = (UIView *)touch.view;
if([view isKindOfClass:[<YOUR_BUTTON> class]]) {
return NO;
}
return YES;
}
OR you can give some tag to button like following
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
UIView *view = (UIView *)touch.view;
if(view.tag == 10000) {
return NO;
}
return YES;
}