We currently are using a solution similar to the one mentioned here (see Ares's answer). This does not seem to work in iOS8.
I have a form sheet, and I want to dismiss it as soon as the user taps on the dimmed view 'behind' the form sheet.
Previously, this seemed possible by adding a gesture recogniser to the window, and checking tap-location to see if it was outside the current form sheet;
I also noticed the point needs to be converted (switch x and y) of the device is used in landscape mode. Other than that, right now it only receives gestures that occurred from inside the form sheet, where before any tap gesture anywhere on the screen would trigger an event.
- (void)viewDidLoad
{
[super viewDidLoad];
self.recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapBehind:)];
self.recognizer.numberOfTapsRequired = 1;
self.recognizer.cancelsTouchesInView = NO;
[self.view.window addGestureRecognizer:self.recognizer];
}
- (void)handleTapBehind:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [sender locationInView:nil];
if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation) && IOS8)
{
location = CGPointMake(location.y, location.x);
}
// if tap outside pincode inputscreen
BOOL inView = [self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil];
if (!inView)
{
[self.view.window removeGestureRecognizer:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
}
As mentioned in the thread you referenced, you should add a UIGestureRecognizerDelegate and implement the methods:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
Related
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];
}
}
Please help me with the below scenario.
Self.view (Swipe Gesture added here)
UicollectionView Object in subView (Default pan,swipe gestures are there)
Want to disable scrollview scrolling/failCollectionViewPan gesture for 2 finger swipe event
Means collection view will not scroll if 2 finger swipe executed
Another approach can be to disable Collection view scrolling while 2 fingers used. Over here, I want not to swipe collection view on 2 finger swipe.
I have implemented this code, but its slowing down scrolling.
[self.collectionView.panGestureRecognizer shouldBeRequiredToFailByGestureRecognizer:_swipeL];
Then I have implemented below codes
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
Now both, Swipe and Scroll are working together.
Then, I have tried in a below manner, but still not got fixed.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]]){
if(gestureRecognizer.numberOfTouches==2){
if( [[otherGestureRecognizer.view class] isSubclassOfClass:[UITableViewCell class]] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewCellScrollView"] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewWrapperView"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPanGestureRecognizer"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPagingSwipeGestureRecognizer"])
{
NSLog(#"Allow&Disable %#", [otherGestureRecognizer description]);
[gestureRecognizer requireGestureRecognizerToFail:otherGestureRecognizer];
return NO;
}
}
}
return YES;
}
Also I have implemented below 2 methods to fix it but fails.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
Its is able to fix for ScrollView with such ways, but the same ways are not working for UICollectionView. Because of, colection view's default pan gesture, it cannot be able to modified. While tring this way, app crashed.
If you want to detect pan gesture, try something like this. But you need to recognise direction of pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGesture:)];
panGesture.delegate = self;
[self.yourCollectionView addGestureRecognizer:panGesture];
}
- (void)panGesture:(UIPanGestureRecognizer *)gesture;
{
if (gesture.numberOfTouches == 2) {
NSLog(#"Two fingers pan gesture");
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
Your problem with UISwipeGestureRecognizer is, that it fires later than UIPanGesture, so in the shouldRecognizeSimultaneouslyWithGestureRecognizer delegate call, swipe gesture is always otherGestureRecognizer, and UIPanGesture is always gestureRecognizer, and in this function you can only disable otherGestureRecoginzer...
UPDATE:
Another solution: use another UIPanGestureRecognizer to disable scroll pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeGesture:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionDown;
swipeGesture.delegate = self;
swipeGesture.numberOfTouchesRequired = 2;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:nil];
panGesture.delegate = self;
[self.tableView addGestureRecognizer:panGesture];
[self.tableView addGestureRecognizer:swipeGesture];
}
- (void)swipeGesture:(UIPanGestureRecognizer *)gesture;
{
NSLog(#"Two fingers swipe gesture");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
This answer might be late, but I did have a similar problem today and couldn't find a solution, until I figured something out myself. It's quite simple, actually.
Swift 5
We create a gesture
let gesture = UIPanGestureRecognizer(target: self, action: #selector(functionCall))
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
We then assign that gesture to the collectionView, effectively overwriting the scroll gesture on it.
collectionView.addGestureRecognizer(gesture) // simply overwrites the 2 finger gesture on the collectionView
It now is possible to scroll the collectionView with 1, 3 or more fingers, but the 2 finger pan gesture is blocked.
I have this snippet of code in the viewDidLoad of my ViewController
UILongPressGestureRecognizer* gestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(_handleLongPress:)];
gestureRecognizer.numberOfTouchesRequired = 1;
gestureRecognizer.minimumPressDuration = 5.0;
gestureRecognizer.delegate = self;
[self.view addGestureRecognizer:gestureRecognizer];
NSLog(#"Gesture recognizers = %#",self.view.gestureRecognizers);
and the methods
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return YES;
}
- (void) _handleLongPress:(UIGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateBegan)
NSLog(#"Started");
}
When I run this app and click on the view for 5 seconds the _handleLongPress gets trigger, however when I run the same app on the device it never hits the _handleLongPress method.. what am I missing? I do see the output that there is the long press :
2014-08-01 10:00:48.505 TestConsole[1343:60b] Gesture recognizers = (
"UILongPressGestureRecognizer: 0x14644d30; state = Possible; view = UIView 0x1453f950>; target= <(action= _handleLongPress:, target=MyViewController 0x1463a730>)>"
I put breakpoints at gestureRecognizer:shouldReceiveTouch: and it does hit that the UILongPressGestureRecognizer.. I am dumbfounded!
I have a gesture recognizer on my view controller which I use to slide in/out the navigation bar. Unfortunately there is a bad NSZombie that makes the whole app crash when I go swipe to go to the previous controller. It's very difficult to track because it only happens in a specific view controller.
Here is the error:
[MyViewController gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]: message sent to deallocated instance
And here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIPanGestureRecognizer *pgr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pgr:)];
pgr.delegate = self;
[self.view addGestureRecognizer:pgr];
}
- (void)pgr:(UIPanGestureRecognizer *)gesture {
// Check if this is the first touch
if (gesture.state == UIGestureRecognizerStateBegan) {
CGPoint point = [gesture locationInView:gesture.view];
self.start = point.y;
}
CGPoint point = [gesture locationInView:gesture.view];
self.currentX = point.x;
self.offsetY = point.y - self.start;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
return YES;
}
return NO;
}
The delegate method is
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
Try to use
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
return YES;
}
return NO;
}