iOS Custom gesture recognizer measure length of longpress - ios

In my app i want to create a custom gesture recognizer that recognizes a long press followed by a swipe.
I need to measure if the length of the long press is more than 1 second. If it is, then call a function and wait for the swipe action to begin.
My problem is that I the only way i know now how long the press was is by extracting the timestamp of touchesBegan from touchesMoved. However i want to know that elapsed time before touchesMoved gets called.
Is there a way to know the length of the tap before the touchesMoved is called?
Thanks in advance!

you can use may code, it can use, but maybe you should deal with some details
in the .h file you should add these ivar:
TestView *aView ;//the view which you press
NSThread *timerThread;
NSTimer *touchTimer;
NSDate *touchStartTime;
CGPoint touchStartPoint;
CGPoint lastTouchPoint;
in the .m file you should add these method:
- (void)doSomething
{
NSLog(#"Long press!!!");
}
- (void)startTimerThead{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
touchTimer = [NSTimer scheduledTimerWithTimeInterval:0.2
target:self
selector:#selector(checkTouchTime:)
userInfo:nil repeats:YES];
[runLoop run];
[pool release];
}
- (void)stopTouchTimer{
if (touchTimer != nil) {
[touchTimer invalidate];
touchTimer = nil;
}
if (timerThread != nil) {
[timerThread cancel];
[timerThread release];
timerThread = nil;
}
}
#define DELETE_ACTIVING_TIME 1.0
- (void)checkTouchTime:(NSTimer*)timer{
NSDate *nowDate = [NSDate date];
NSTimeInterval didTouchTime = [nowDate timeIntervalSinceDate:touchStartTime];
if (didTouchTime > DELETE_ACTIVING_TIME){
[self stopTouchTimer];
[self doSomething];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
UIView *touchView = [touch view];
if ([touchView isKindOfClass:[TestView class]]) {
touchStartTime = [[NSDate date] retain];
if (nil == timerThread) {
timerThread = [[NSThread alloc] initWithTarget:self selector:#selector(startTimerThead) object:nil];
[timerThread start];
}
}
touchStartPoint = [touch locationInView:self.view];
lastTouchPoint = touchStartPoint;
}
#define TOUCH_MOVE_EFFECT_DIST 10.0f
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint movedPoint = [touch locationInView:self.view];
CGPoint deltaVector = CGPointMake(movedPoint.x - touchStartPoint.x, movedPoint.y - touchStartPoint.y);
if (fabsf(deltaVector.x) > TOUCH_MOVE_EFFECT_DIST
|| fabsf(deltaVector.y) > TOUCH_MOVE_EFFECT_DIST)
{
[self stopTouchTimer];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
UIView *touchView = [touch view];
CGPoint movedPoint = [touch locationInView:self.view];
CGPoint deltaVector = CGPointMake(movedPoint.x - touchStartPoint.x, movedPoint.y - touchStartPoint.y);
if ([touchView isKindOfClass:[TestView class]]) {
if (fabsf(deltaVector.x) < TOUCH_MOVE_EFFECT_DIST
&& fabsf(deltaVector.y) < TOUCH_MOVE_EFFECT_DIST) {
[self stopTouchTimer];
}
}
}

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.

iOS: How do I keep a UIImageView from resetting after a scoring update?

The game I'm trying to make is simple enough. A UIImageView fireball falls from the top of the screen, and the touch screen is used to move the other UIImageView Dustin to avoid it.
Once the fireball reaches the bottom of the screen, it resets back to the top, and the user's score increases by 1. The problem is that after I added the scoring functions, the Dustin UIImage resets to its original position after the fireball reaches the bottom. Without the scoring method, Dustin's image doesn't reset. How does the scoring cause my imageview's position to reset? Most of my code from the ViewController is below.
int theScore;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initializeTimer];
}
- (void)updateScore {
theScore = theScore + 1;
_score.text = [[NSString alloc] initWithFormat:#"%d",theScore];
}
- (void) initializeTimer {
if(theTimer == nil)
{
theTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLogic:)];
}
theTimer.frameInterval = 1;
[theTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) gameLogic:(CADisplayLink *) theTimer {
if(!CGRectIntersectsRect(_fireball.frame, _Dustin.frame)){
if(_fireball.center.y >600){
_fireball.center = CGPointMake(0, 0);
[self updateScore];
}
_fireball.center = CGPointMake(_fireball.center.x, _fireball.center.y+4);
}
else{
}
}
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event {
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}

iOS: How can I make this game reset button work properly?

I have a game where a fireball UIImage falls from the screen, and the user moves the player object with the touch screen to avoid them. After 8 points, the playAgainButton pops up. However, this playAgainButton button isn't working. It doesn't execute any of the code in the ResetGame method, and it actually resets the position of my character object "Dustin", which I don't want to happen. I have a referencing outlet attached to the button. How can I get the code in ResetGame to work? My ViewController code is below.
int theScore;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initializeTimer];
}
- (IBAction)ResetGame:(id)sender { //only thing that actually happens right now is that Dustin resets for some reason.
theScore = 0;
[self updateScore];
_Fireball.center = CGPointMake(64, 64);
_endLabel.hidden = true;
_playAgainButton.hidden = true;
[self gameLogic:theTimer];
}
- (void)updateScore {
_score.text = [[NSString alloc] initWithFormat:#"%d",theScore];
}
- (void) initializeTimer {
if(theTimer == nil)
{
theTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLogic:)];
}
theTimer.frameInterval = 1;
[theTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) gameLogic:(CADisplayLink *) theTimer {
if((!CGRectIntersectsRect(_Fireball.frame, _Dustin.frame)) && (theScore < 8)){
if(_Fireball.center.y >600){
int num = arc4random() % 3;
if(num == 0){
_Fireball.center = CGPointMake(64, 64);
}
else if(num == 1){
_Fireball.center = CGPointMake(192, 64);
}
else if(num == 2){
_Fireball.center = CGPointMake(320, 64);
}
theScore = theScore + 1;
[self updateScore];
}
_Fireball.center = CGPointMake(_Fireball.center.x, _Fireball.center.y+12); }
else if(theScore == 8){
_Fireball.center = CGPointMake(_Fireball.center.x, _Fireball.center.y);
_endLabel.text = #"You Win!";
_endLabel.hidden = false;
_playAgainButton.hidden = false;
}
}
- (void) touchesBegan:(NSSet *) touches withEvent:(UIEvent *) event {
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
_firstTouch = [touch locationInView:self.view];
_lastTouch = [touch locationInView:self.view];
_Dustin.center = CGPointMake(_firstTouch.x, _Dustin.center.y);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Your touchesBegan etc. methods are not calling super and thus all touches are being captured by your app and cannot get through to your button.
Add a call to super to each of these methods.

Detect objects, touchesmoved and UITouch

I have a UIView on where i add balls with a ID. When i touch the ball with the ID 1 i draw a dashed line follows my finger.
The problem if when i move the finger to the other balls, i don't know how to detect these balls and check the ID they have. The what i need to happen is when i touch the next ball with the ID 2, the line finish draw and stay from ball 1 to ball 2, and create new one from 2 to finger, next.. 3 etc...
The code:
#import "DrawView.h"
#implementation DrawView
- (id)init
{
self = [super initWithFrame:CGRectMake(0, 0, 1024, 768)];
if (self) {
self.backgroundColor = [UIColor clearColor];
self.opaque = YES;
checkPointCircle = [[CheckPointCircle alloc] initWithPosX:315 posY:138 andNumber:1];
checkPointCircle.userInteractionEnabled = YES;
[self addSubview:checkPointCircle];
checkPointCircle = [[CheckPointCircle alloc] initWithPosX:706 posY:138 andNumber:2];
checkPointCircle.userInteractionEnabled = YES;
[self addSubview:checkPointCircle];
checkPointCircle = [[CheckPointCircle alloc] initWithPosX:315 posY:526 andNumber:3];
checkPointCircle.userInteractionEnabled = YES;
[self addSubview:checkPointCircle];
checkPointCircle = [[CheckPointCircle alloc] initWithPosX:706 posY:526 andNumber:4];
checkPointCircle.userInteractionEnabled = YES;
[self addSubview:checkPointCircle];
}
return self;
}
- (Line *)drawLine {
return drawLine;
}
- (void)setDrawLine:(Line *)line
{
drawLine = line;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CheckPointCircle * checkTouch = (CheckPointCircle *)touch.view;
if (touch.view.class == checkPointCircle.class) {
if ([checkTouch getObjectID] == 1) {
startTouchPoint = CGPointMake([checkTouch center].x, [checkTouch center].y);
endTouchPoint = [touch locationInView:self];
}
}
self.drawLine = [[Line alloc] initWithPoint:[touch locationInView:self]];
[self setNeedsDisplay];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Cancelado");
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
UITouch *secondaryTouch = (UITouch *)[[[event touchesForView:checkPointCircle] allObjects] objectAtIndex: 0];
NSLog(#"Que toco: %# ", secondaryTouch.view);
if (touch.view.class == checkPointCircle.class) {
CheckPointCircle * checkTouch = (CheckPointCircle *)touch.view;
if ([checkTouch getObjectID] == 1) {
endTouchPoint = [touch locationInView:self];
}
}
[self setNeedsDisplay];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CheckPointCircle * checkTouch = (CheckPointCircle *)touch.view;
if (touch.view.class == checkPointCircle.class) {
if ([checkTouch getObjectID] == 1) {
endTouchPoint = [touch locationInView:self];
}
}
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
[drawLine drawLineFrom:startTouchPoint to:endTouchPoint];
}
#end
How to detect the other balls to get the ID.
Can anybody help me?
Thanks!
The Apple documentation of class UIView (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/hitTest:withEvent:) lists two methods which will determine which view contains a hit/point:
– hitTest:withEvent:
– pointInside:withEvent:
Probably hitTest will help most: Its description reads "Returns the farthest descendant of the receiver in the view hierarchy (including itself) that contains a specified point.", so it even converts coordinates as needed.
If you check [self hitTest: [touch.locationInView self] withEvent: event] (or similar, no code completion here ;-)= in your touchesMoved: method, you should be able to detect when the finger is over any of the other circles.
Hope this helps, nobi

CGRect and touch

In this cocos2d app the nslog is not firing when I press the ccsprite. Could someone help me?
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets) {
CGRect targetRect = CGRectMake(target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
27,
40);
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
if (CGRectContainsPoint(targetRect, touchLocation)) {
NSLog(#"Moo cheese!");
}
}
return YES;
}
First of all be sure that you register the sprite for touches into the onEnter method for example:
- (void)onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:defaultTouchPriority_ swallowsTouches:YES];
[super onEnter];
}
This will make your sprite touchable and so fire the event to the sprite when a user will press it.
Then refactor your code to make it more readable and test something like that:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
NSArray *targetsToDelete = [self touchedSpritesAtLocation:touchLocation];
// Put your code here
// ...
return YES;
}
- (NSArray *)touchedSpritesAtLocation:(CGPoint)location
{
NSMutableArray *touchedSprites = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets)
if (CGRectContainsPoint(target.boundingBox, location))
[touchedSprites addObject:target];
return [touchedSprites autorelease];
}
It should return the targets that have been touched.
In layer init method add this
self.isTouchEnabled = true;
Use this code for touch detection
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CGRect rect = [self getSpriteRect:yourSprite];
if (CGRectContainsPoint(rect, location))
{
NSLog(#"Sprite touched\n");
}
}
To get sprite rect:
-(CGRect)getSpriteRect:(CCNode *)inSprite
{
CGRect sprRect = CGRectMake(
inSprite.position.x - inSprite.contentSize.width*inSprite.anchorPoint.x,
inSprite.position.y - inSprite.contentSize.height*inSprite.anchorPoint.y,
inSprite.contentSize.width,
inSprite.contentSize.height
);
return sprRect;
}

Resources