Two UILongPressGestureRecognizer, one can fire, the other cannot - iphone

guys,
I have added two UILongPressGestureRecognizer. What I want is when two buttons are pressed down for 0.3 second, fire up "shortPressHandler". If user keeps pressing these two buttons for another 1.2 second, fire up "longPressHandler". Now I only get shortPressHandler fired up and the longPressHandler was never fired. I think it might because the shortPressGesture is recognized first and longPressGesture never gets a chance. Can anyone show me how to achieve what I want? Thanks in advance.
UILongPressGestureRecognizer *longPressGesture =[[[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)] autorelease];
longPressGesture.numberOfTouchesRequired = 2;
longPressGesture.minimumPressDuration = 1.5;
longPressGesture.allowableMovement = 10;
longPressGesture.cancelsTouchesInView = NO;
longPressGesture.enabled = true;
[self.view addGestureRecognizer:longPressGesture];
UILongPressGestureRecognizer *shortPressGesture =[[[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(shortPressHandler:)] autorelease];
shortPressGesture.numberOfTouchesRequired = 2;
shortPressGesture.minimumPressDuration = 0.3;
shortPressGesture.allowableMovement = 10;
shortPressGesture.cancelsTouchesInView = NO;
shortPressGesture.enabled = true;
[self.view addGestureRecognizer:shortPressGesture];

Insert this line before adding shortPressGesture:
[shortPressGesture requireGestureRecognizerToFail:longPressGesture];
Note: shortGesture will not get called immediately after 0.3 seconds of holding but when the tap is released if it was between 0.3 seconds and 1.2 seconds long. If the tap is longer than 1.2 s (you have 1.5s in your code which is probably a typo) only longPressGesture will fire up.
EDIT:
However, if you want your event handlers both to fire (in an event of long press), you should do this:
Your UIView of should implement the <UIGestureRecognizerDelegate> in .h file.
In .m file you add this method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Now instead of adding the line:
[shortPressGesture requireGestureRecognizerToFail:longPressGesture];
You add this two lines:
shortPressGesture.delegate = self;
longPressGesture.delegate = self;
NOTE: if you have any other UIGestureRecognisers linked to your UIVIew you will have to add some checking in shouldRecognizeSimultaneouslyWithGestureRecognizer:, otherwise you can simply return YES.

Related

NSTimer scheduledTimerWithTimeInterval and CoreAnimation [duplicate]

This question already has an answer here:
How to make marquee UILabel / UITextField / NSTextField
(1 answer)
Closed 9 years ago.
I am writing an app using a cocoa control that animates text,
https://www.cocoacontrols.com/controls/marqueelabel
It uses CoreText and QuartzCore.
It's a pretty great control, but i'm having trouble.
When instantiated the animated labels that I create using the control start to scroll immediately, however I'm finding that when I create an animated label using NSTimer it is not animating.
I am using
- (void)viewDidLoad
{
[super viewDidLoad];
....
[self.view addSubview:continuousLabel1];
[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(updatePrice)
userInfo:nil repeats:YES];
}
to call a method that creates the animated label, when I call the method directly the labels create and animate, however when called using the above scheduled timer, new labels created only animate on the first call, not on the timers repeat calls to the method.
I have checked that my method is being call on the main thread/ main runloop, any ideas?
For clarity the work flow is:
Timer calls method --> Label 1 is created and starts to Animate.
5 Seconds Late...
Timer calls method again --> Label 2 is created However is does not begin to animate as expected.
EDIT:
- (void)updatePrice
{
MarqueeLabel *continuousLabel2 = [[MarqueeLabel alloc] initWithFrame:CGRectMake(10, 230, self.view.frame.size.width-20, 20) rate:100.0f andFadeLength:10.0f];
continuousLabel2.tag = 101;
continuousLabel2.marqueeType = MLContinuous;
continuousLabel2.animationCurve = UIViewAnimationOptionCurveLinear;
continuousLabel2.continuousMarqueeExtraBuffer = 50.0f;
continuousLabel2.numberOfLines = 1;
continuousLabel2.opaque = NO;
continuousLabel2.enabled = YES;
continuousLabel2.shadowOffset = CGSizeMake(0.0, -1.0);
continuousLabel2.textAlignment = NSTextAlignmentLeft;
continuousLabel2.textColor = [UIColor colorWithRed:0.234 green:0.234 blue:0.234 alpha:1.000];
continuousLabel2.backgroundColor = [UIColor clearColor];
continuousLabel2.font = [UIFont fontWithName:#"Helvetica-Bold" size:17.000];
continuousLabel2.text = #"This is another long label that doesn't scroll continuously with a custom space between labels! You can also tap it to pause and unpause it!";
[self.view addSubview:continuousLabel2];
if( [NSThread isMainThread])
{
NSLog(#"In Main Thread");
}
if ([NSRunLoop currentRunLoop] == [NSRunLoop mainRunLoop]) {
NSLog(#"In Main Loop");
}
}
The problem is that you are calling updatePrice every 5 seconds and in this method you are creating a new label, that that is stacking up, and notice that it is going darker and darker every 5 seconds.
And if you check the label is scrolling.
Solution:
If you want multiple instances of the label change its coordinates.
If you want single instance then compare check and create it just once.
You should read the section Important Animation Note from the README file.
My guess is that you will need to call controllerLabelsShouldAnimate: passing your current view controller to animate those labels added after the view controller has already been shown.

UILongPressGestureRecognizer on UITableViewCell does not work in iOS 5 and 4.3

I have a table view, and I use UILongPressGestureRecognizer on table view cell to show a context menu over the cell to allow user perform some extra functionality. Everything is work well in iOS 5.1, but when I test in iOS 5 and 4.3, the event is not fired.
Does anyone know how to solve this problem please help me, thanks in advance.
Below is my code:
in tableViewCell.h: add UIGestureRecognizerDelegate
in tableViewCell.m
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(handleLongPress:)];
longPressRecognizer.minimumPressDuration = 1.5;
longPressRecognizer.numberOfTouchesRequired = 1;
longPressRecognizer.numberOfTapsRequired = 0;
longPressRecognizer.delegate = self;
[self addGestureRecognizer:longPressRecognizer];
[longPressRecognizer release];
// Method to handle event
- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
// Do something.
}
}
After spending 1.5 days on this issue, I discover that somehow the tableView receives long press event but tableViewCell does not on iOS 5/4.3 . So I fixed this issue by adding UILongPressGuestureRecognizer to tableView then in the long press event handler then call tableViewCell to show the context menu, and it works.
I met this problem too. I found that long press gesture recognizer works fine only when 'delegate' property is 'NULL'. So just try to remove this line.
longPressRecognizer.delegate = self;

UILongPressGestureReconizer Tag? - iPhone

I'm setting up a LPGR like so and I was wonder if I can create a tag in each LPGR. I need to do this so I know which of all my buttons is being pressed...
UILongPressGestureRecognizer *longpressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
longpressGesture.minimumPressDuration = 2;
[longpressGesture setDelegate:self];
[pushButton addGestureRecognizer:longpressGesture];
And my method below...
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
NSLog(#"longPressHandler");
}
I know you can't pass arguments via selectors, so I was wondering if I could assign a tag to the LPGR or if in the method I could grab the tag of the button that was using the LPGR? Is any of this possible>?
EDIT:
NSInteger *tag = [gestureRecognizer.view.tag];
NSLog(#"%# longPressHandler",tag);
UIGestureRecognizer has a property view which is the view the gesture recognizer is attached to.
Therefore, in your handler method, gestureRecognizer.view is the button that the LPGR is attached to and gestureRecognizer.view.tag is the button's tag.
ADDED:
Sample code:
- (void)longPressHandler:(UILongPressGestureRecognizer *)gestureRecognizer {
NSLog(#"longPressHandler");
NSInteger tag = gestureRecognizer.view.tag;
NSLog(#"%d longPressHandler",tag);
}
You can simply create a subclass of UILongPressGestureRecognizer and add a tag property to it. You could also use associated objects to add the property with a category.

iPhone: Combining MKMapView with another UITapGestureRecognizer

i am trying to implement my own gesture recognizer in addition to the one already used by the MKMapView. Right now i can tap on the map and set a pin. This behavior is realized by my UITapGestureRecognizer. When i tap on a pin that already exists, my gesture recognizer does nothing, but instead the callout bubble of this pin is shown. The UIGestureRecognizerDelegate looks like this:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (gestureRecognizer == self.tapRecognizer)
{
bool hitAnnotation = false;
int count = [self.mapView.annotations count];
int counter = 0;
while (counter < count && hitAnnotation == false )
{
if (touch.view == [self.mapView viewForAnnotation:[self.mapView.annotations objectAtIndex:counter]])
{
hitAnnotation = true;
}
counter++;
}
if (hitAnnotation)
{
return NO;
}
}
return YES;
}
This works fine. My only problem are the callout bubbles of the pins and the double tap. Normally the double tap is used for zooming in. This still works but in addition to this, i also get a new pin. Is there any way to avoid this?
The other problem occurs with the callout bubble of a pin. I can open the bubble by tapping on the pin without setting a new pin at this place (see code above) but when i want to close the bubble by tapping on it, another pin is set. My problem is, that i cannot check with touch.view , if the user tapped on a callout bubble, because it is not a regular UIView as far as i know. Any ideas or workarounds for this problem?
Thanks
I had the same problem as your first problem: distinguishing double taps from single taps in an MKMapView. What I did was the following:
[doubleTapper release];
doubleTapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(mapDoubleTapped:)];
doubleTapper.numberOfTapsRequired = 2;
doubleTapper.delaysTouchesBegan = NO;
doubleTapper.delaysTouchesEnded = NO;
doubleTapper.cancelsTouchesInView = NO;
doubleTapper.delegate = self;
[mapTapper release];
mapTapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(mapTapped:)];
mapTapper.numberOfTapsRequired = 1;
mapTapper.delaysTouchesBegan = NO;
mapTapper.delaysTouchesEnded = NO;
mapTapper.cancelsTouchesInView = NO;
[mapTapper requireGestureRecognizerToFail:doubleTapper];
and then implemented the following delegate method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Using requireGestureRecognizerToFail: allows the app to distinguish single taps from double taps and implementing gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: ensures that double taps are still forwarded to the MKMapView so that it continues zooming normally. Note that doubleTapper doesn't actually do anything (in my case, except log debug messages). It's simply a dummy UIGestureRecognizer that's used to help separate single taps from double taps.

filtering single and double taps

When the user single taps my view, i need one specific method to run.
When the user double taps, i need another method do take place.
The problem is that the double tap triggers the single tap, and it introduce bugs in my logic.
I can't use UIGestureRecognizer because i need to keep track of the points.
I try some booleans, but no chance. I also tried the cancel/perfomSelector-delay technique, but it does not work (that's strange because other folks on other forums said it works, maybe the simulator touch detection is different ?)
I'm trying to let the user set the position (drag, rotate) of a board piece, but i need to be aware of piece intersections, clip to the board area, etc, that's why a simple boolean will not solve the problem.
Thanks in advance!
Check this, seems right what you are looking for:
Below the code to use UITapGestureRecognizer to handle single tap and double tap. The code is designed that it won't fire single tap event if we got double tap event. The trick is use requireGestureRecognizerToFail to ask the single tap gesture wait for double tap event failed before it fire. So when the user tap on the screen, the single tap gesture recognizer will not fire the event until the double tap gesture recognizer. If the double tap gesture recognizer recognize the event, it will fire a double tab event and skip the single tap event, otherwise it will fire a single tap event.
UITapGestureRecognizer *doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleDoubleTap:)];
doubleTapGestureRecognizer.numberOfTapsRequired = 2;
//tapGestureRecognizer.delegate = self;
[self addGestureRecognizer:doubleTapGestureRecognizer];
//
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTap:)];
singleTapGestureRecognizer.numberOfTapsRequired = 1;
[singleTapGestureRecognizer requireGestureRecognizerToFail: doubleTapGestureRecognizer];
//tapGestureRecognizer.delegate = self;
[self addGestureRecognizer:singleTapGestureRecognizer];
Possibly I don't understand exactly what you mean by "i need to keep track of the points", but I don't see any problems with doing this with a gesture recognizer.
Swift 3 Solution:
doubleTap = UITapGestureRecognizer(target: self, action:#selector(self.doubleTapAction(_:)))
doubleTap.numberOfTapsRequired = 2
singleTap = UITapGestureRecognizer(target: self, action:#selector(self.singleTapAction(_:)))
singleTap.numberOfTapsRequired = 1
singleTap.require(toFail: doubleTap)
self.view.addGestureRecognizer(doubletap)
self.view.addGestureRecognizer(singleTap)
In the code line singleTap.require(toFail: doubleTap) we are forcing the single tap to wait and ensure that the tap even is not a double tap. Which means we are asking to ensure the double tap event has failed hence it is concluded as a single tap.
Swift Solution :
doubleTapSmall1.numberOfTapsRequired = 2
doubleTapSmall1.addTarget(self, action: "doubleTapPic1")
smallProfilePic1.addGestureRecognizer(doubleTapSmall1)
singleTapSmall1.numberOfTapsRequired = 1
singleTapSmall1.addTarget(self, action: "singleTapPic1")
singleTapSmall1.requireGestureRecognizerToFail(doubleTapSmall1)
smallProfilePic1.addGestureRecognizer(singleTapSmall1)
Just implement the UIGestureRecognizer delegate methods by setting the delegate properly.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Also add this line of code
[singleTap requireGestureRecognizerToFail:doubleTap];