Detect only double or single tap with UIViews? - ios

I want to detect JUST double / single tap when the user touches a view.
I made something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint prevLoc = [touch ]
if(touch.tapCount == 2)
NSLog(#"tapCount 2");
else if(touch.tapCount == 1)
NSLog(#"tapCount 1");
}
But it always detect 1 tap before 2 taps. How can I detect just 1 / 2 tap?

Thanks for you help. I also found a way like that:
-(void)handleSingleTap
{
NSLog(#"tapCount 1");
}
-(void)handleDoubleTap
{
NSLog(#"tapCount 2");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSUInteger numTaps = [[touches anyObject] tapCount];
float delay = 0.2;
if (numTaps < 2)
{
[self performSelector:#selector(handleSingleTap) withObject:nil afterDelay:delay ];
[self.nextResponder touchesEnded:touches withEvent:event];
}
else if(numTaps == 2)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(handleDoubleTap) withObject:nil afterDelay:delay ];
}
}

It will help to define methods for single and double taps
(void) handleSingleTap {}
(void) handleDoubleTap {}
So then in touchesEnded you can call the appropriate method based on the number of taps, but only call handleSingleTap after a delay to make sure double tap has not been executed:
-(void) touchesEnded(NSSet *)touches withEvent:(UIEvent *)event {
if ([touch tapCount] == 1) {
[self performSelector:#selector(handleSingleTap) withObject:nil
afterDelay:0.3]; //delay of 0.3 seconds
} else if([touch tapCount] == 2) {
[self handleDoubleTap];
}
}
In touchesBegan, cancel all requests to handleSingleTap so that the second tap cancels the first tap's call to handleSingleTap and only handleDoubleTap will be called
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(handleSingleTap) object:nil];

Maby you can use some time interval. Wait with dispatching the event for (x)ms. If you get two taps in that time-period, dispatch a double-tap. If you only get one- dispatch single tap.

Related

Neither touchesEnded nor touchesCancelled not called sometimes after touchesMoved

I Have one circular view in my application and I am moving some objects like graph in that circular view its work fine in 90% cases but at some cases its not calling TouchEnded and my reset graphs code is in TouchEnded methods so its unloaded at some point below is my code for touches delegate methods.
#pragma mark - Touch Events For Rotation of GRAPH
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch* touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
prevAngle = [Utilities angleBetweenPoint:touchPoint toPoint:self.view.center];
/// convert negative angle into positive angle
if(prevAngle < 0){
prevAngle = PI_DOUBLE + prevAngle;
}
//
[_viewStaticRadialPart hideToolTip];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGFloat diffAngle, tempCurAngle, tempPrevAngle, curTransformAngle, newTransformAngle;
NSLog(#"touchesMoved");
// Get the only touch (multipleTouchEnabled is NO)
UITouch* touch = [touches anyObject];
// Track the touch
CGPoint touchPoint = [touch locationInView:self.view];
curAngle = [Utilities angleBetweenPoint:touchPoint toPoint:self.view.center];
/// convert negative angle into positive angle
if(curAngle < 0){
curAngle = PI_DOUBLE + curAngle;
}
prevAngle = curAngle;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self resetViewsOnceRotationStops];
}
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
[resetViewsAfterTouchStops invalidate];
resetViewsAfterTouchStops = nil;
}
I am stuck here since last night so any help will be appreciated.
Thanks in advance.
I don't know the cause for this problem from core level.
But NSTimer can help us in
But I feel we can resolve this by adding setting / replace NSTimer instance while touchesMoved called and we can reset that timer on touchesEnded and touchesCancelled. So, in either case, your touchesEnded or touchesCancelled not called timer will do that job and your reset logic works as it should be expected.
Demo Source Code
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
NSLog(#"touchesMoved");
//Timer re-initialize code here
if (resetViewsAfterTouchStops) {
[resetViewsAfterTouchStops invalidate];
resetViewsAfterTouchStops = nil;
}
resetViewsAfterTouchStops = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(resetViewsOnceRotationStops) userInfo:nil repeats:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
isTouched = NO;
[resetViewsAfterTouchStops invalidate];
resetViewsAfterTouchStops = nil;
[self resetViewsOnceRotationStops];
[self setUserInteractionOnSectorsAs:YES];
}
- (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event
{
NSLog(#"touchesCancelled");
if(resetViewsAfterTouchStops)
{
[self resetViewsOnceRotationStops];
[self setUserInteractionOnSectorsAs:YES];
}
[resetViewsAfterTouchStops invalidate];
resetViewsAfterTouchStops = nil;
}
- (void) resetViewsOnceRotationStops
{
//your reset code will be place here
}
Try removing UIGestureRecognizer objects on that view if you are using any. UIGestureRecognizer objects interfere with touch events lifecycle.

touchesEnd does not get called bit touchesBegin and touchesMoved does get called

I'm trying to do a drag in ios. If the drag movement was a short distance, I was going to count the drag as a click event, by comparing the start x,y (from touchesBegan) to the x,y in touchesEnd.
It seems like touchesEnd never gets called. I put a break point to verify it and the break point never went off.
code:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSUInteger touchCount = [touches count];
NSUInteger tapCount = [[touches anyObject] tapCount];
[ self UpdateDrag];
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSUInteger touchCount = [touches count];
NSUInteger tapCount = [[touches anyObject] tapCount];
}
- (void) touchesEnd:(NSSet *)touches withEvent:(UIEvent *)event {
// this methes never gets called
NSUInteger touchCount = [touches count];
NSUInteger tapCount = [[touches anyObject] tapCount];
if (mDisplay==nil)
{
mDisplay = [[cDisplayYesOrNo_iPhone alloc]
initWithNibName:#"cDisplayYesOrNo_iPhone"
bundle:[NSBundle mainBundle]];
}
[ mDisplay SetUp];
[self presentModalViewController: mDisplay animated:NO];
}
Is - (void)touchesEnded: not - (void)touchesEnd:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIResponder_Class/Reference/Reference.html#//apple_ref/occ/instm/UIResponder/touchesEnded:withEvent:
You need to edit touchesEnd to touchesEnded.
Also, for completeness, you need to consider whether you also need to respond to touchesCancelled which is called instead of touchesEnded in some circumstances.

cocos2d for iphone touch detection on a sprite

I am using ccTouchesBegan and ccTouchesEnded methods to register touches. Everything was ok until I placed some buttons(ccmenuitems) on my node. now when i place my finger down on a button and then move it to any other place of screen ccTouchesEnded method does not calls. What am I doing wrong? How can I detect touches on a button?
some code:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [self convertTouchToNodeSpace: [touches anyObject]];
// here i check if touch is in the right place
if ([self ptInRect:location :CGRectMake(0, center.y - 160, winSize.width, 40)]) {
dragBeginLocation = location;
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [self convertTouchToNodeSpace: [touches anyObject]];
if (ABS(location.x - dragBeginLocation.x) < 8) {
NSLog(#"TAP");
} else {
NSLog(#"SWIPE");
}
}
So, when I begin touch on a sprite and release on background I get nothing in console.
Buttons are in CCMenu, which is higher than background.

ios touch move from one button to another button

I have two custom uibuttons.
#interface myButton : UIButton
I overwrite several methods like
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// do something
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self.nextResponder touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self];
if (!CGRectContainsPoint(self.bounds, touchPoint)) {
[self touchesCancelled:touches withEvent:event];
}
return;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// do something
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
return;
}
The thing I want is , when I touch a button, I can keep moving my touch to another button. I tired to call touchesCancelled when the touch is out of the bound of the first button I touched. So I "think" then I move to another button it will be a new touch event. But it doesn't work like this.
Did I do something wrong? Or the touchesCancelled method is not used for like this?
Thanks in advance!
Your suspesions are correct, this is not how touchesCancelled:withEvent: was intended to be used. From the documentation:
This method is invoked when the Cocoa Touch framework receives a system interruption requiring cancellation of the touch event; for this, it generates a UITouch object with a phase of UITouchPhaseCancel. The interruption is something that might cause the application to be no longer active or the view to be removed from the window.
Your responder will receive the touches cancelled event if your user receives an incoming phone call, SMS or their alarm goes off etc. It should be used to clean up any state information that was established in the other touch events.
It seems as though you want to change the responder associated with the touch events mid-touch i.e. when you drag the touch from the bounds of the first button and enter that of the second button it should become the responder recieving the touch events. Unfortunately that is not the way responders are designed to work. Once a UIView is identified as being the responder, as returned in hitTest:withEvent:, this will be the UIView to receive touch events.
A possible workout to achieve what you want would be to handle the touch events (touchesBegan:withEvent:, touchesMoved:withEvent: etc.) in a superview that contains both of your buttons. Then your superview will receive the touch events and you can take action depending on which button's frame you are within:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint touchLocation = [touch locationInView:self.view];
UIButton *button;
if (CGRectContainsPoint(self.button1.frame, touchLocation))
{
button = self.button1;
NSLog(#"touchesMoved: First Button");
}
else if (CGRectContainsPoint(self.button2.frame, touchLocation))
{
button = self.button2;
NSLog(#"touchesMoved: Second Button");
}
// Do something with button...
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint touchLocation = [touch locationInView:self.view];
UIButton *button;
if (CGRectContainsPoint(self.button1.frame, touchLocation))
{
button = self.button1;
NSLog(#"touchesMoved: First Button");
}
else if (CGRectContainsPoint(self.button2.frame, touchLocation))
{
button = self.button2;
NSLog(#"touchesMoved: Second Button");
}
// Do something with button...
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint touchLocation = [touch locationInView:self.view];
UIButton *button;
if (CGRectContainsPoint(self.button1.frame, touchLocation))
{
button = self.button1;
NSLog(#"touchesEnded: First Button");
}
else if (CGRectContainsPoint(self.button2.frame, touchLocation))
{
button = self.button2;
NSLog(#"touchesEnded: Second Button");
}
// Do something with button...
}
}

How to get these methods to work with multi-touch?

I found this code on a tutorial which pretty much sets a left or right side BOOL to YES if the touch started on a specific side of the screen and then checks when the touch moves to see if it changes sides on the screen in order to make the other BOOL yes.
So I am now trying to implement multi-touch but I am not sure how it would work with the following code? Does anyone have any idea how I would go upon it?
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
touchStartPoint = [touch locationInView:self.view];
if (touchStartPoint.x < 160.0) {
touchLeftDown = TRUE;
}
else if (touchStartPoint.x > 160.0) {
touchRightDown = TRUE;
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentTouchPoint = [touch locationInView:self.view];
if (touchStartPoint.x < 160.0 && currentTouchPoint.x < 160.0) {
touchLeftDown = TRUE;
}
else if (touchStartPoint.x > 160.0 && currentTouchPoint.x > 160.0)
{
touchRightDown = TRUE;
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLeftDown = touchRightDown = FALSE;
}
Thanks!
Edit1:
These are what the bools are doing in the game loop, pretty much what I am trying to achieve is that if there is a touch on both sides at the same time, the TOUCH_INCREMENT will be 0 since the touches on each side will cancel each other out. How would I achieve that? Anyway this is the code I am talking about:
if (touchLeftDown == TRUE) {
touchStep -= TOUCH_INCREMENT;
}
else if (touchRightDown == TRUE) {
touchStep += TOUCH_INCREMENT;
}
else {
touchStep = SLOWDOWN_FACTOR * touchStep;
}
touchStep = MIN(MAX(touchStep, -MAX_ABS_X_STEP), MAX_ABS_X_STEP);
pos.x += touchStep;
You can probably make this work without the touchStartPoint (i)var. The important thing is to not use -anyObject and instead to inspect each touch. The following code modifications might work for you:
-(void) countTouches:(NSSet *)touches withEvent:(UIEvent *)event{
int leftTouches=0;
int rightTouches=0;
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInView:touch.view];
//just in case some code uses touchStartPoint
touchStartPoint=location;
if (location.x < 160.0) {
leftTouches++;
}
else if (location.x > 160.0) {
rightTouches++;
}
}
//reset touch state
touchLeftDown=FALSE;
//set touch state if touch found
if(leftTouches>0){
touchLeftDown=TRUE;
}
touchRightDown=FALSE;
if(rightTouches>0){
touchRightDown=TRUE;
}
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self countTouches:touches withEvent:event];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self countTouches:touches withEvent:event];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLeftDown = touchRightDown = FALSE;
}
Here I have created a function that is called by touchesBegan and touchesMoved because they need to implement the same logic. You may see some unintended side effects if touchStartPoint is being used in the code somehow.

Resources