Unable to trigger longPress gesture [UiView] - ios

I'm quite new to iOS and objective c in particular and I'm facing an issue were I'm unable to properly register a long press gesture recognizer to my view. I'm a little bit confused because I already added a tap gesture recognizer in a similar way and it worked fine.
This is how I add my long press gesture recognizer :
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action: #selector(onTap:)];
UILongPressGestureRecognizer * longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action: #selector(longPress:)];
[_pdfView addGestureRecognizer:longPressGesture];
[_pdfView addGestureRecognizer:tapGesture];
This is my longPress function:
-(void) longPress:(UILongPressGestureRecognizer *)sender {
NSLog(#"longPress");
}
This is my header file :
#interface FLTPDFSandboxController : NSObject <FlutterPlatformView, CustomPdfViewDelegate>
- (instancetype)initWithFrame:(CGRect)frame;
-(void) onTap:(UITapGestureRecognizer *)sender;
-(void) longPress:(UILongPressGestureRecognizer *)sender;
- (UIView*)view;
#end
The tap gesture is working fine for me. It is just de long press one that is not triggered consistently. In fact in some very rare times it is triggered in a somewhat random manner.
Thank you.

Related

iOS distinguish single tap and double tap at first opening

I know that I can use requireGestureRecognizerToFail function to distinguish single tap and double tap, but I meet a tiny problem, and I want to fix it. My code is as below:
- (IBAction)singleTap:(UITapGestureRecognizer *)sender {
NSLOGD_METADATAONLY();
hideNavigationBar();
hideStatusBar();
[sender requireGestureRecognizerToFail:self.doubleTapRecognizer];
}
- (IBAction)doubleTap:(UITapGestureRecognizer *)sender {
NSLOGD_METADATAONLY();
//TODO
}
When I open a file and double tap(action1), single tap(result1) will be called firstly and then double tap(result2) will be called.
But if I open a file and single tap(action3), then double tap(action4), the result of action4 is working well,single tap will not be called only double tap will be called. I guess it is because in action3 the function requireGestureRecognizerToFail is called.
My question is HOW CAN I make action1 just call result2 and not call result1?
Write the below lines in ViewDidLoad
[singleTap requireGestureRecognizerToFail:doubleTap];
Extend your class as UIGestureRecognizerDelegate.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
Then assign your view controller as delegate of gesture recognizer.
tapGesture .delegate = self;
doubleTapGesture .delegate = self;
Implementation of shouldRequireFailureOf prevents the one gesture from being recognized until after the other gesture recognizer explicitly reaches the failed state.
You need to recognize a single tap only after the failure of a double tap, which happens when the touch sequence contains only one tap.
This can be achieved implementing following.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// Don't recognize a single tap until a double-tap fails.
if gestureRecognizer == self.tapGesture &&
otherGestureRecognizer == self.doubleTapGesture {
return true
}
return false
}
Hope it helps.Happy Coding!!
Update your code with [singleTapRecognizer requireGestureRecognizerToFail: doubleTapRecognizer] when you creating UITapGestureRecognizer
Example:
// single tap
UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: #selector(handleSingleTapOnView:)];
[singleTapRecognizer setNumberOfTouchesRequired:1];
[singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
[view addGestureRecognizer: singleTapRecognizer];
// double tap
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget: tableViewController action: #selector (handleDoubleTapOnView:)];
[doubleTapRecognizer setNumberOfTouchesRequired:2];
[view addGestureRecognizer: doubleTapRecognizer];
When you set requireGestureRecognizerToFail: for a gesture recognizer, you're saying that it should only recognize the gesture if the other gesture recognizer did not.

Limit Gesture Recognizer to Only One Specific UIView?

I have a UIView called myView on myViewController. I have a UIGestureRecognizer called swipeLeft (code below) that detects when a user swipes left on it.
The problem is: myViewController recognises the same gesture on the whole screen and performs another action. So I would like my app to perform myMethod when you swipeLeft in this particular area of myViewController, the area being myView.
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(myMethod:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.delaysTouchesBegan = YES;
[self.myView addGestureRecognizer:swipeLeft];
More details: I am using RESideMenu and myViewController is the right menu, so when it is visible, the whole view of myViewController recognises swipes in all directions. I would like to change the recogniser in this particular UIView myView.
Thanks!
First you will need to add the swipe gesture to your view controllers header file.
#property (strong, nonatomic) UISwipeGestureRecognizer *swipeLeft;
If you look in DEMORootViewController.m, you will see this call:
self.rightMenuViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"rightMenuViewController"];
This will call your awakeFromNib, and it's your first chance to do something. In here you will create that swipe gesture. You can not add it to your view yet though, because your outlets are not set at this point. The first time they are set is in viewDidLoad, so that's where you will add the gesture to your view. So add this to your view controllers implementation file
- (void)awakeFromNib
{
self.swipeLeft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(myMethod:)];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.swipeView addGestureRecognizer:self.swipeLeft];
}
- (void)myMethod:(UISwipeGestureRecognizer *)swipe
{
NSLog(#"Did swipe") ;
}
Finally you will need to tell the pan gesture in RESideMenu.m to fail whenever our swipeLeft gesture occurs. The way to do that is to add the following code to RESideMenu.m at line 220. That's at the end of the viewDidLoad method.
if ([self.rightMenuViewController isKindOfClass:[DEMOVC class]]) {
DEMOVC *rightVC = (DEMOVC *)self.rightMenuViewController;
if (rightVC.swipeLeft) {
[panGestureRecognizer requireGestureRecognizerToFail:rightVC.swipeGesture];
} } }
This is assuming your custom VC is called DEMOVC. You will also need to import your custom VC to RESideMenu.m like this:
#import "DEMOVC.h"
Please let me know if this worked out for you, and if there's something else I can help you with.

Hide keyboard on touch anywhere outside UITextField when view includes UICollectionView

There are some answers out there such as this, but in the case where there is a UIScrollView or UICollectionView present, it doesn't work.
The touchesBegan method on the viewController will never get called.
On screen, I have a UITextField at the top.
Below that, filling up the rest of the screen is a UICollectionView.
I need to dismiss the keyboard if I touch anywhere besides the UITextField (including the collection view obviously)
So what is the best way to do this?
For such a common UI paradigm it seems like there should be a well-known solution, but I've yet to come across it.
To dismiss Keyboard on tap of the View: Add a Tap gesture to your ViewController.collectionView as follows:
//declare a property to store your current responder
#property (nonatomic, assign) id currentResponder;
//in viewDidLoad:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(resignOnTap:)];
[singleTap setNumberOfTapsRequired:1];
[singleTap setNumberOfTouchesRequired:1];
[self.collectionView addGestureRecognizer:singleTap];
//Implement the below delegate method:
- (void)textFieldDidBeginEditing:(UITextField *)textField {
self.currentResponder = textField;
}
//Implement resignOnTap:
- (void)resignOnTap:(id)sender {
[self.currentResponder resignFirstResponder];
}
The simple way to do it is:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
Here's a better solution which doesn't require adding gesture recognisers individually to everything. It's in Swift, but could easily be converted to ObjC.
Add the following to your viewDidLoad():
let tap = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
and then add the following method declaration:
func dismissKeyboard()
{
view.endEditing(true)
}
...where view is your text field.

Detect long press on UIButton in iOS Simulator

I have a UIButton in my Custom UITableViewCell. I am working on some control events on that button in the UITableViewCell by the following code. This is taken from CellForRowAtIndexPath method.
cell.gestureButton.tag = indexPath.row ;
[cell.gestureButton addTarget:self action:#selector(cellTapped:) forControlEvents:UIControlEventTouchUpInside];
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(cellLongPressed:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self ;
[cell.gestureButton addGestureRecognizer:lpgr];
I am testing this on iOS 7 Simulator. My problem is , for the first event when UIControlEventTouchUpInside is executed , I can see the result and my cellTapped method is called properly.
But in the second case where I have assigned a UILongPressGestureRecognizer on my button , I can't see the result in simulator and cellLongPressed: method is never called. As far I understand, my code is ok. So, I would like to know , where's the problem ? Is there any problem with my code or Simulator doesn't support this feature ? Thanks in advance for your help.
I'm betting lpgr is conflicting with another gesture recognizer. Have you tried implementing UILongPressGestureRecognizer's delegate methods? You may need to set up a failure dependency. Specifically, you'll probably need to return YES in gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:.
Make sure your cellLongPressed is declared as following.
- (void)cellLongPressed:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"cellLongPressed in action");
}
Or if its declared as following:
- (void)cellLongPressed {
NSLog(#"cellLongPressed in action");
}
Please change your gesture initializer to following:
UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(cellLongPressed)];
Note there is no ":" at the end of cellLongPressed selector name.

Bad access on UITapGestureRecognizer

I'm trying to add an UITapGestureRecognizer on my UIView. This is a part of the code of my UIViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// Create and initialize a tap gesture
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(respondToTapGesture:)];
// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;
// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];
}
-(void) respondToTapGesture:(UITapGestureRecognizer *)gr {
NSLog(#"It's working !");
}
The problem is that when I tap on the view, I have this message :
0x396e25d0: ldr r3, [r4, #8] < Thread 1 : EXC_BAD_ACCESS (code=1, address=0x8)
Does anyone has an idea ?
One of the possibilities of this error is if you are doing this on a modal view or something like that and it has already deallocced. Be sure to remove the gesture recognizer from self.view.window before going back/dismissing the viewcontroller that has the uiGestureRecognizer.
Has something to do with allocation or delegate methods!

Resources