How to create touch events for MKMapView? - iphone

How to create Touch Events for MKMapView.
I'm using UIViewController and adding MKMapView on that using interface builder.
Now I need to handle touch events for that map.....
I tried by writing UITouch Delegate methods
But I failed...It is not getting called.
Please post a solution how to handle touch events on MKMapView.....
Thanks in advance...

If you are happy with an iOS 4 and above solution, I've used UIGesture recognisers and never had a problem.
Here's an example for a long pressure gesture (tap and hold):
// Long press gesture recogniser
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleLongPressGesture:)];
[self.view addGestureRecognizer:longPressGesture];
[longPressGesture release];
And then you can handle the even in your handleLongPressGesture: method:
-(void)handleLongPressGesture:(UILongPressGestureRecognizer*)sender
{
if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateChanged)
return;
else {
// Your app logic here...
}
}

Related

UIGestureRecognizerStateBegan calling twice in iOS 6

Giving thanks in advance, I would like to share the strange behaviour of UILongPressGestureRecognizer.
UIView *v = [UIView alloc] initWithFrame:CGRectMake(0,0,20,20)];
UILongPressGestureRecognizer *longpressGesture1 = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[v addGestureRecognizer:longpressGesture1];
and here the delegate to handle the gesture recogniser.
-(IBAction)tapped:(UILongPressGestureRecognizer *) gesture
{
switch ([gesture state]) {
case UIGestureRecognizerStateBegan:
{
NSLog(#"Long Tap detacted.");
}
break;
case UIGestureRecognizerStateChanged:
{
NSLog(#"UIGestureRecognizerStateChanged");
}
break;
case UIGestureRecognizerStateEnded:
{
NSLog(#"Long Tap ended.");
}
break;
}
}
This piece of code is working perfectly as expected in iOS 4 and 5, but in ios 6 with retina display when we perform the long tap, UIGestureRecognizerStateBegan is being called twice for a single long tap resulting in a application crash.
Any help is greatly welcome.
UILongPressGestureRecognizer is a continuous event recognizer. You have to look at the state to see if this is the start, middle or end of the event and act accordingly.
Its calling two times because you are pressing and removing your finger.
First call is indicating you that there is an Long tap detected i.e. UIGestureRecognizerStateBegan
Second call is indicating you that there is end of that tap i.e. UIGestureRecognizerStateBegan
There are three state of tap
UIGestureRecognizerStateBegan
UIGestureRecognizerStateChanged
UIGestureRecognizerStateEnded
If you will drag your finger then it will called multiple time that will indicate that there is some changes in its state.
Follow UILongPressGestureRecognizer Class Reference for more

Stopping a UIGestureRecognizer from calling selector

I'm adding in a few UIGestureRecognizers with a target and selector. I'll just talk about one since the other will be the same i'm sure.
I've added a UIPinchGestureRecognizer
UIPinchGestureRecognizer *pinch = [UIPinchGestureRecognizer new];
[pinch addTarget:self action:#selector(pinchGestureDetected:)];
[self.view setMultipleTouchEnabled:YES];
[self.view addGestureRecognizer:pinch];
Now my goal here is to simply call this method once when I receive a pinch gesture. But obvoiusly it continues to call it as the person pinches. I'm using it as part of a page navigation and will be updating the view when a pinch in is detected.
So in my -(void)pinchGestureDetected:(UIPinchGestureRecognizer)pinch method I'll be calling another method. Kinda like ... and this is a little sudo
-(void)pinchGestureDetected:(UIPinchGestureRecognizer)pinch
{
if (pinch.scale > 1) layoutViewWithMoreDetail;
else layoutViewWithLessDetail;
}
So I don't want it to keep calling this method or the layout method will continue to be called. I want one layout / pinch gesture.
Is there a way I can stop detecting the pinch once it has determined the scale?? Something along the way of ...
-(void)pinchGestureDetected:(UIPinchGestureRecognizer)pinch
{
if (pinch.scale > 1)
{
layoutViewWithMoreDetail;
stop receiving pinch gestures till this.gesture is finished;
}
Would I impliment the GestureDelegate??
-(void)pinchGestureDetected:(UIPinchGestureRecognizer)pinch
{
if (pinch.scale > 1 && pinching == NO )
{
layoutViewWithMoreDetail;
pinching = YES;
}
and then in the delegate for Gesture ended ... pinching = NO;
Thanks for any help
UPinchGestureRecognizer is a continuous gesture - use if(pinch.state == UIGestureRecognizerStateBegan) to detect whether or not the event is just now starting.
However, this results in a low threshold for triggering the event. An alternate method is to quicky disable and enable a gesture when it has been triggered to your satisfaction, like so:
-(void)pinchGestureDetected:(UIPinchGestureRecognizer)pinch
{
if (pinch.scale > 1)
{
//do your stuff here
pinch.enabled = NO;
pinch.enabled = YES;
}
}
This is because if you look at the documentation, it states:
enabled
A Boolean property that indicates whether the gesture recognizer is enabled.
#property(nonatomic, getter=isEnabled) BOOL enabled
Discussion
Disables a gesture recognizers so it does not receive touches. The default value is YES. If you change this property to NO while a gesture recognizer is currently recognizing a gesture, the gesture recognizer transitions to a cancelled state.

handle taps in two different points at a same time via UIGestureRecognizer

I have two labels in two different positions, when both labels are tapped at the same time i want another label to show a success message.
How do I accomplish this? I can recognize a single tap or double tap with one or more finger touches but this is a different scenario. Please help. I tried this, but it does not work.
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 2;
tapRecognizer.delegate = self;
[self.view addGestureRecognizer:tapRecognizer];
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view == tap2 && touch.view == tap1)
{
result.text = #"success";
}
return YES;
}
Thanks in advance.
What you're trying to detect isn't really a single gesture.
I'd suggest adding a tap gesture recogniser to each button. The handler would:
Store the time of the tap (at the moment that the handler is called)
Compare this time with the time that the other button was last
tapped. If the times are very similar (perhaps 0.25 secs apart),
consider that they've both been tapped simultaneously and react
accordingly.
Play with the time interval on a real device to find the ideal amount.
UPDATE:
A code snippet that obviously hasn't been tested in any way:
- (void)handleButton1Tap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded)
{
self.button1TapTime = CACurrentMediaTime();
[self testForSimultaneousTap];
}
}
- (void)handleButton2Tap:(UITapGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateEnded)
{
self.button2TapTime = CACurrentMediaTime();
[self testForSimultaneousTap];
}
}
- (void)testForSimultaneousTap
{
if (fabs(self.button1TapTime - self.button2TapTime) <= 0.2)
{
// Do stuff
}
}
where self.button1TapTime and self.button2TapTime are member variables (doubles).
Tim
Formally I had accepted termes's answer first and that worked too, but I have found a more simpler solution to this process. There is no need for two gesture recognizers, it is achievable with a simple tap gesture recognizer with number of touches count to two. Here is the code:
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapRecognizer.numberOfTapsRequired = 1;
tapRecognizer.numberOfTouchesRequired = 2;
tapRecognizer.delegate = self;
[self addGestureRecognizer:tapRecognizer];
Now, in the handle tap method we can easily get the two touch points by "locationOfTouch:inView:", a instance method of UIGestureRecognizer class. So in the handleTap: method we need to check if the two touch points are in the desired location. Here is the code:
-(void)handleTap:(UITapGestureRecognizer*)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint point1 = [recognizer locationOfTouch:0 self];
CGPoint point2 = [recognizer locationOfTouch:1 self];
if ([self validateTapIn:point1 and:point2])
{
resultLabel.text = #"success";
}
}
}
-(BOOL)validateTapIn:(CGPoint)point1 and:(CGPoint)point2
{
return
(CGRectContainsPoint(label1.frame, point1) && CGRectContainsPoint(label2.frame,:point2)) ||
(CGRectContainsPoint(label1.frame, point2) && CGRectContainsPoint(label2.frame, point1));
}

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;

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.