I'm working on an application using a map on which the user have to add an annotation. Very simple. Problem is, right after I added a tap recognizer on my map, the zoom supposed to be triggered after a double tap no longer works. It seems to be a pretty common issue, as I found (and tried, I swear) a lot of potential solutions without any success.
The latest solution I tried (and I felt very clever after finding it by myself) was trying to retrieve the "built-in double tap" recognizer of the MkMapView, so I can reuse it in my "single tap" gesture recognizer. This is what I did :
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *gestures = [map gestureRecognizers];
for (UIGestureRecognizer *r in gestures) {
if ([r class] == [UITapGestureRecognizer class]) {
if (2 == [(UITapGestureRecognizer *)r numberOfTapsRequired]) {
builtInDoubleTap = (UITapGestureRecognizer *)r;
NSLog(#"BOOM! Found it!");
return;
}
}
}
NSLog(#"Guess view did load");
[self enableTapRecognizer];
}
And in my "enableTapRecognizer" method :
- (void)enableTapRecognizer {
UITapGestureRecognizer *mapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handlePosition:)];
if(builtInDoubleTap) {
NSLog(#"require to fail builtInDoubleTap");
[mapGestureRecognizer requireGestureRecognizerToFail: builtInDoubleTap];
}
[map addGestureRecognizer: mapGestureRecognizer];
}
Problem is, it never shows my "Boom! Found it!" debug message. How come it can zoom after a double tap without any gesture recognizer? That doesn't really make sense, does it?
(I also tried using a UIGestureRecognizerDelegate so my gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer method returns YES...)
I'm kinda desperate right now, so if somebody has an idea... maybe something wrong in my map configuration?
I'm targeting iOS5 and running latest xCode.
Thanks !
Related
I have a very strange issue, UITextView touch event crash on double tap whereas same code works with < iOS10 version. (It means below iOS10 version there is no crash for press gesture recognizer)
Actually, I am adding the double tap and log press gesture based on permission. If the user has permission to comment then add gestures in viewDidLoad methods. Comment is allowed only with double tap or long press
singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureAction:)];
singleTapGesture.numberOfTapsRequired = 1;
// adding gesture to open window for commenting only when he has writing access
if (canComment) {
longPressgesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressGestureAction:)];
longPressgesture.minimumPressDuration = 0.2;
doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(doDoubleTap:)];
doubleTap.numberOfTapsRequired = 2;
}
On single tap
-(void)singleTapGestureAction:(UITapGestureRecognizer*)tapGestureRecognizer{
if (isSingleTapped) {
isSingleTapped = NO;
return;
}
isSingleTapped = YES;
UITextView *textView = (UITextView *)tapGestureRecognizer.view;
[self.commentView becomeFirstResponder]; // becomeFirstResponder
}
On double tap
-(void)doDoubleTap:(UITapGestureRecognizer*)tapGestureRecognizer
{
UITextView *textView = (UITextView *)tapGestureRecognizer.view;
[self.commentView becomeFirstResponder]; // becomeFirstResponder
// To show the UIMenuController menu
[self setCommentMenuToolTipWithRect:completeRect];
}
NOTE: I am adding [self.commentView becomeFirstResponder]; on every gesture action
UITextView delegate methods
- (void)textViewDidBeginEditing:(UITextView *)inView
{
[self.commentView becomeFirstResponder];
range=[self.commentView selectedRange];
}
USE CASE:
When I double tap to select any word then APP CRASH and UIMenuController does not appear,
but if I add the following line app does not crash
- (void)textViewDidChangeSelection:(UITextView *)textView{
[textView resignFirstResponder];
} // app does not crash
and UIMenuController appears with comment menu items that's great. I was happy that I have fixed the crash issue.
But there is another problem, when I press outside, menu hides and
select any word AGAIN then It does not appear SECOND time.
I have tried all the possible way to show the menu for returns
YES/TRUE to canBecomeFirstResponder. I know, there has to be a view
that claims firstResponder for the menu to show. but how ?
On second time touch, not even calling any gesture recognizer method
From the logs it is clear that when double tap is recognized, same touch update is also sent to another gesture recognizer, which fails.
So, a simple solution would be to avoid detection of other gestures on double tap.
This can simply be achieved by making all other gestures on commentView require doubleTap to fail using requireGestureRecognizerToFail. just add the condition in addGestureToTextView method as shown below.
if (withDoubleTap && self.canScreenPlayEdit) {
[self.commentView removeGestureRecognizer:singleTapGesture];
[self.commentView addGestureRecognizer:doubleTap];
[self.commentView addGestureRecognizer:longPressgesture];
for (UIGestureRecognizer *recognizer in self.commentView.gestureRecognizers) {
[recognizer requireGestureRecognizerToFail:doubleTap];
}
}
This does solve the crash and also shows the menu without calling resignFirstResponder in textViewDidChangeSelection.
However, there seem to be many issues in your code. PLSceneDetailsVC is too complicated and you need to simplify the code. You need to streamline the gesture management or you will end up facing many more such issues.
longPressgesture.minimumPressDuration = 0.2;
My guess the problem is here. 0.2s is way too small to be used for longPress. Probably both were triggered (longPress and double tap).
Change it to higher like 1.5s.
On one of the UIViewControllers of my iPhone app, I have a UIPanGestureRecognizer attached so that when the user swipes to the left or to the right the app advances or goes back one screen. However, the problem is that when the user taps (rather than swipes) on the screen it still advances. How can I stop this from happening. I have pasted the relevant code below:
-(void) addGestureRecognizer
{
UIPanGestureRecognizer *pan;
pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(swipeRecognized:)];
[pan setMinimumNumberOfTouches:1];
[self.view addGestureRecognizer:pan];
}
-(void) swipeRecognized: (UIPanGestureRecognizer *) recognizer
{
if(recognizer.state != UIGestureRecognizerStateBegan) return;
CGPoint velocity = [recognizer velocityInView:self.view];
if(velocity.x > 0)
{
[self.navigationController popViewControllerAnimated:YES];
}
else
{
#try
{
[self performSegueWithIdentifier:NEXT_STEP_SEGUE sender:self];
}
#catch (NSException *exception)
{
//Silently die...muhaha
}
}
}
I would suggest using a UISwipeGestureRecognizer for swipes. Is there a particular reason you're use a pan?
With a UISwipeGestureRecognizer you can specify for which direction it should recognize the gesture.
It's also better for your users, to use the appropriate gestures. That way, they'll feel right at home :)
You should either use a UISwipeGestureRecognizer, as suggested by fguchelaar, or use the translationInView: method to figure out how far the user has actually moved the finger (for taps, that should be near zero).
You also shouldn't return early if the state is not UIGestureRecognizerStateBegan (first line in your method), otherwise your method will only get called once, when the gesture begins, but not during the gesture. If that's what you actually want, a UISwipeGestureRecognizer is all you need. The benefit of the pan gesture recognizer is mostly that you can track the user's finger and provide direct feedback (like moving the view while the finger is still down).
I use MFSideMenu in my application, and i can show the menu using a pan gesture on the navigation bar only. I would like it to work on the whole screen, like on the facebook app.!
I've tried changing this line (l.39 in MFSideMenuManager.m)
[controller.navigationBar addGestureRecognizer:recognizer];
to this :
[controller.view addGestureRecognizer:recognizer];
but it just won't work.
Do you have any idea of what i should edit for it to work?
Thank you for your help
I finally succeeded making it work. The gesture is actually already implemented but working only if the menu is hidden. We have to remove 2 conditions to make sure it works both ways
There are two lines to edit in the MFSideMenuManager.m
In the gestureRecognizerShouldBegin: method
if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
if([gestureRecognizer.view isEqual:self.navigationController.view] &&
self.navigationController.menuState != MFSideMenuStateHidden) return YES;
becomes
if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
if([gestureRecognizer.view isEqual:self.navigationController.view]) return YES;
In the navigationControllerPanned: method, just remove the if line
- (void) navigationControllerPanned:(id)sender {
if(self.navigationController.menuState == MFSideMenuStateHidden) return;
[self handleNavigationBarPan:sender];
}
becomes
- (void) navigationControllerPanned:(id)sender {
[self handleNavigationBarPan:sender];
}
And it works!
It is not a really good practice to edit a library, but it is easy if you want to go further to add a boolean option to MFSideMenu to make it configurable.
I don't know the MFSideMenuManager but if the bar is draggable I expect it to have a UIPanGestureRecognizer with a line
[self.navigationController.navigationBar addGestureRecognizer:gestureRecognizer];
So what you do is replace the navigation bar with the view for the whole navigation controller
[self.navigationController.view addGestureRecognizer:gestureRecognizer];
Using the GestureRecognizer attached to a view triggers my app to crash with EXC_BAD_ACCESS error. Here's the classes involved
• BoardViewController - Displaying a board (as background) set as rootViewController in the AppDelegate. It instantiates multiple objects of the "TaskViewcontroller".
//BoardViewController.h
#interface BoardViewController : UIViewController {
NSMutableArray* allTaskViews; //for storing taskViews to avoid having them autoreleased
}
//BoardViewController.m - Rootviewcontroller, instantiating TaskViews
- (void)viewDidLoad
{
[super viewDidLoad];
TaskViewController* taskA = [[TaskViewController alloc]init];
[allTaskViews addObject:taskA];
[[self view]addSubview:[taskA view]];
}
• TaskViewController - An indivual box displayed on the board. It should be draggable. Therefore I attached UIPanGestureRecoginzer to its view
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
[[self view] addGestureRecognizer:panRecognizer];
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
NSLog(#"PAN!");
}
The .xib file is a simple view.
All programming with the gesture recognizer I'd prefer to do in code. Any idea how to fix the error causing the app crash?
The method handlePan is on your view controller, not on your view. You should set the target to self:
UIPanGestureRecognizer* panRecognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(handlePan:)];
EDIT (in response to the edit of the question) As omz has correctly noted, your TaskViewController gets released upon BoardViewController's viewDidLoad: exit. There are two ways of dealing with it:
Fold the handlePan method into the parent view controller, along with the code of viewDidLoad:, or
Make an instance variable for TaskViewController *taskA, rather than making it a local variable.
This is my way to use Gesture Recognizer. I think this way is easy and with low risk.
At first, you drag and drop Gesture Recognizer into the view.
Then, you wire the Gesture Recognizer icon to code.
Finally, you write code for this IBAction like below:
- (IBAction)handlePan:(id)sender {
NSLog(#"PAN!");
}
You can download this project from GitHub and just run it.
https://github.com/weed/p120812_PanGesture
here's the problem:
I'd like to move to using UIGestureRecognizer in my Apps.
For this reason I'd like to ditch TouchBegan/TouchEnded event's from my views.
However I don't understand how to manage when the touch began (user puts its finger on the screen) with UIGestureRecognizers.
The simplest one is UITapGestureRecognizer but the selector associated gets fired only when the TapGesture is completed (Well... it makes completely sense of course). But still the problem remains: how can I stop using touchesBegan and get that event anyway from UIGestureRecognizer?
Thanks!
Here is an example:
//Pan gesture
recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
((UIPanGestureRecognizer *)recognizer).minimumNumberOfTouches = 3; //number of fingers
recognizer.delegate = self;
[self.view addGestureRecognizer:recognizer];
[recognizer release];
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan)
{
//do something
} else if (recognizer.state == UIGestureRecognizerStateEnded)
{
//do something
}
}
Also implement UIGestureRecognizerDelegate in .h file. May be you need to do self.view.userInteractionEnabled = YES depending on the view you're using. e.g., if it's UIImageView, the you need to set userInteractionEnabled = YES, default is NO
For what you are tryin ti do you can't. The gesture recoginizers are for high level gestures so they behaive the same across all apps (think swipes, the timing required for a double tap, etc). For low level control and to do things that the recognizers can't you will still have to implement logic in touchesbegan, touchesEnded, etc.
Why not implement your own touchesBegan in a UIGestureRecognizer subclass -- intercept the message, extract the information you'd like, and then pass the message along to super's touchesBegan?