Rapid tap doesn't fire touchesEnded - iphone

Ok, so I am hoping someone can shed some light on this. The thing is I have an app where the user can interact with a number of "pads" that are kind of like big icons, that can be dragged around on the screen. Tapping once on the pad opens it and tapping and holding brings up an alert view which asks if you want to delete it. I am using a timer to check that a user is tapping and holding and invalidating the timer in the touchesMoved and touchesEnded events. The thing is that if you tap very quickly on a pad the touchesEnded event is never fired and as such the delete dialog appears, which can get a little confusing for a user. Any thoughts on why this is?
- (void) startTouchTimer:(float)delay
{
self.touchTimer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(touchHeld:) userInfo:nil repeats:NO];
}
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if (touches.count == 1)
{
for(WishPad *pad in self.view.subviews)
{
if ([touch view] == pad)
{
//Animate Selection
[UIView animateWithDuration:0.03
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
pad.highlight.alpha = 0.5;
}
completion:NULL];
self.touchedPad = pad.padName;
[self startTouchTimer:0.40];
[self.view bringSubviewToFront:pad];
}
}
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.touchTimer invalidate];
UITouch *touch = [[event allTouches] anyObject];
if (touches.count == 1)
{
for(WishPad *pad in self.view.subviews)
{
if ([touch view] == pad)
{
CGPoint location = [touch locationInView:self.view];
pad.center =location;
}
}
}
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event
{
[self.touchTimer invalidate];
UITouch *touch = [[event allTouches] anyObject];
if (touches.count == 1)
{
for(WishPad *pad in self.view.subviews)
{
if ([touch view] == pad)
{
//Animate deselection
[UIView animateWithDuration:0.03
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
pad.highlight.alpha = 0;
}
completion:NULL];
CGPoint location = pad.frame.origin;
NSString *position = NSStringFromCGPoint(location);
iWishAppDelegate *delegate = (iWishAppDelegate *)[[UIApplication sharedApplication] delegate];
for (Wish in [delegate.wishes objectForKey:#"<none>"])
{
if ([position isEqual:Wish.position] && [[(WishPad*)[touch view] padName] isEqualToString:Wish.name])
{
self.goToAddWish = [[AddWish alloc] initWithNibName:#"AddWish" bundle:nil];
self.goToAddWish.editWish=YES;
self.goToAddWish.hidesBottomBarWhenPushed=YES;
[self.navigationController pushViewController:self.goToAddWish animated:YES];
[self.goToAddWish editDetailText:Wish];
[self.goToAddWish release];
}
else if ([pad.padName isEqual:Wish.name])
{
Wish.position = position;
[delegate save];
}
}
}
}
}
}
- (void) touchHeld:(NSTimer*)timer
{
[self.touchTimer invalidate];
NSString *wishToDel = [NSString stringWithFormat:#"Delete %#?", self.touchedPad];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:wishToDel message:#"Do you really want to delete this wish?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Delete", nil];
[alertView show];
[alertView release];
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == [alertView cancelButtonIndex])
{
//Lots of deleting from plist and deleting image stuff happening here
}

You can try to use UIGestureRecognizers instead.
You would have to set up the gesture recognizer for all your pads
for(WishPad *pad in self.view.subviews)
{
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(padTapped:)];
[pad addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
UILongPressGestureRecognizer *pressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(padPressed:)];
[pad addGestureRecognizer:pressRecognizer];
[pressRecognizer release];
}
and the you can handle the taps and presses
- (void)padTapped:(UITapGestureRecognizer *)recognizer {
WishPad *pad = (WishPad *)recognizer.view;
// handle tap for pad
}
- (void)padPressed:(UILongPressGestureRecognizer *)recognizer {
WishPad *pad = (WishPad *)recognizer.view;
// handle long press for pad
}
You can control how long a press should take to be recognized as a press using minimumPressDuration. But just read up on UIGestureRecognizer. It can save you a ton of work.

Related

moving multiple images in iOS after being created programmatically using tags

I am trying to programmatically create multiple images then be able to move any of them around the view. I click a button and then I can move that image. I click the button again and I can move that image but no longer can move the first image created. I was trying to use tags.
header.
UIImageView *imageView;
NSUInteger i;
and
#property (nonatomic, retain) UIImageView *imageView;
implementation
#synthesize imageView;
-(IBAction)printTheImage:(id)sender{
UIImageView *theImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"animage.png"]];
self.imageView = theImage;
self.imageView.userInteractionEnabled = YES;
self.imageView.frame = CGRectMake(500, 500, 200, 200);
imageView.tag = i;
[self.view addSubview:self.imageView];
i++;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
CGPoint location = [touch locationInView:self.view];
if ([touch view]==self.imageView)
{
if (ImageView.tag == 1){
self.imageView.center = location;
}
if (imageView.tag == 2){
self.imageView.center = location;
}
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[self touchesBegan:touches withEvent:event];
}
- (void)viewDidLoad{
i = 1;
[super viewDidLoad];
}
Here's what I came up with from your code
//header
NSUInteger imageCount;
#property (nonatomic, retain) UIImageView *selectedImageView;
//implementation
#synthesize selectedImageView;
-(IBAction)printTheImage:(id)sender {
UIImageView *imageView = [[UIImageView alloc] initWithImage: [UIImage imageNamed: #"animage"]];
[imageView sizeToFit];
imageView.frame = CGRectMake(500, 500, 200, 200);
imageView.userInteractionEnabled = YES;
imageView.tag = imageCount;
[self.view addSubview: imageView];
[imageView release];
imageCount++;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
//iterate trough all images
for (int i = 0; i < imageCount; i++) {
//get image from tag
UIImageView *imageView = (UIImageView *)[self.view viewWithTag: i];
//check which image was tapped
if (CGRectContainsPoint(imageView.frame, [touch locationInView:self.view])) {
NSLog(#"Image #%i was tapped",i);
self.selectedImageView = imageView;
//don't waste processor time for checking all other images, get the first and break the loop
break;
}
}
This is what moves the image:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
//move the tapped image
self.selectedImageView.center = [touch locationInView:self.view];
}
Remeber to reset the image counter and set selectedImageView to nil on touches ended and touches canceled phase
Try this instead.
#property (nonatomic, copy) NSMutableArray *imageViews;
#synthesize imageView;
-(IBAction)printTheImage:(id)sender{
UIImageView *theImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"animage.png"]];
[self.imageViews adObject:theImage];
theImage.userInteractionEnabled = YES;
theImage.frame = CGRectMake(500, 500, 200, 200);
imageView.tag = [imageViews indexOfObject:theImage];
[self.view addSubview:theImage];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
CGPoint location = [touch locationInView:self.view];
NSInteger viewIndex = [self.imageViews indexOfObject:[touch view]];
if (viewIndex != NSNotFound)
UIView *theImage = self.imageViews[viewIndex];
theImage.center = location;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[self touchesBegan:touches withEvent:event];
}
this approach works with gesture recognizer
#interface ViewController : UIViewController {
int tags;
}
- (IBAction)create:(id)sender;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//Tag to start
tags = 1;
}
- (IBAction)create:(id)sender
{
UIImageView *temp = [[UIImageView alloc] initWithFrame:CGRectMake(0, 100, 200, 200)];
temp.image = [UIImage imageNamed:#"yourimage.jpg"];
temp.userInteractionEnabled = YES;
temp.tag = tags;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handlePan:)];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 1;
[temp addGestureRecognizer:pan];
[temp addGestureRecognizer:tap];
[self.view addSubview:temp];
tags++;
}
//This is just to check if the tags are working
-(void)handleTap:(UITapGestureRecognizer *)recognizer
{
UIView *piece = recognizer.view;
int index = piece.tag;
NSString *msg = [NSString stringWithFormat:#"Tag:%i", index];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Test"
message:msg
delegate:self
cancelButtonTitle:#"ok"
otherButtonTitles:nil, nil];
[alert show];
}
//Drag action
-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
UIView *piece = recognizer.view;
if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged)
{
CGPoint translation = [recognizer translationInView:[piece superview]];
[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
[recognizer setTranslation:CGPointZero inView:[piece superview]];
[self.view bringSubviewToFront:piece];
}
}
#end
already tested and working
good luck

Detect if the user is still holding the touch and didn't release

I wanted to detect the user touch release and if the user is holding it, the code down below
works, but dont tell me if i'm holding ( touch and hold and not releasing ) the touch...
please help me fix that
[imageview setUserInteractionEnabled:YES];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(holdAction:)];
[singleTap setNumberOfTapsRequired:1];
[imageview addGestureRecognizer:singleTap];
- (void)holdAction:(UIGestureRecognizer *)holdRecognizer
{
if (holdRecognizer.state == UIGestureRecognizerStateBegan) {
NSLog(#"Holding Correctly. Release when ready.");
} else if (holdRecognizer.state == UIGestureRecognizerStateEnded)
{
NSLog(#"You let go!");
}
}
Do it with the -touchesBegan:withEvent: and -touchesEnded:withEvent: methods.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.isHolding = YES;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
self.isHolding = NO;
}
Where self.isHolding is a #property (nonatomic, assign) BOOL isHolding;
Note: In those methods you may need to perform additional check if the touches have started over a particular view and also where they have ended.
UPDATE: Change your code accordingly:
[imageview setUserInteractionEnabled:YES];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(holdAction:)];
[imageview addGestureRecognizer:longPress];
- (void)holdAction:(UILongPressGestureRecognizer *)holdRecognizer
{
if (holdRecognizer.state == UIGestureRecognizerStateBegan) {
NSLog(#"Holding Correctly. Release when ready.");
} else if (holdRecognizer.state == UIGestureRecognizerStateEnded)
{
NSLog(#"You let go!");
}
}

How can we able to make table view work when Gesture is applied to self.view?

I have applied gesture on whole view and I want to interact with table view within self.view.I have applied custom gesture.that is as follows:
#import "TouchEvent.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation TouchEvent
#synthesize xInc=_inc;
#synthesize prev=_prev;
#synthesize diff=_diff;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setState:UIGestureRecognizerStateBegan];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setState:UIGestureRecognizerStateCancelled];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
// A tap can have slight movement, but we're not interested
// in a tap. We want more movement. So if a tap is detected
// fail the recognizer.
if ([self state] == UIGestureRecognizerStatePossible) {
[self setState:UIGestureRecognizerStateBegan];
} else {
[self setState:UIGestureRecognizerStateChanged];
}
UIView *view = [self view];
CGPoint touchpoint = [touch locationInView:view];
CGPoint prevPoint=[touch previousLocationInView:view];
if (prevPoint.x<touchpoint.x)
[self setDiff:(touchpoint.x-prevPoint.x)];
else
[self setDiff:(prevPoint.x-touchpoint.x)];
NSLog(#"difference is: %f",self.diff);
NSLog(#"x is: %f",touchpoint.x);
[self setXInc:touchpoint.x];
[self setPrev:prevPoint.x];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setState:UIGestureRecognizerStateEnded];
}
#end
and I m applying like this
- (void)viewDidLoad
{
NSArray *ary=[NSArray arrayWithObjects:#"Friends",#"Message",#"Chats",#"New Feeds",#"Photos",#"Notes", nil];
array=ary;
gestur=[[TouchEvent alloc] initWithTarget:self action:#selector(SLideTheView:)];
[self.view addGestureRecognizer:gestur];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)moveIt:(CGFloat)x_pos
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDelegate:self];
[viewU setFrame:CGRectMake(x_pos, 0, 320, 460)];
[UIView commitAnimations];
}
-(void)SLideTheView:(TouchEvent*)gesture
{
CGPoint pt=[gesture locationInView:viewU];
if (pt.x>13&&pt.y>5&&pt.x<29&&pt.y<23)
{
[self slideView];
}
else
{
if (gesture.state==UIGestureRecognizerStateEnded)
{
if (viewU.frame.origin.x!=0) {
if (gesture.prev<gesture.xInc)
{
[self moveIt:270];
}
else
[self moveIt:0];
}
}
else
{
if (viewU.frame.origin.x!=0)
{
if (gesture.prev<gesture.xInc)
x=viewU.frame.origin.x+gesture.diff;
else
x=viewU.frame.origin.x-gesture.diff;
[viewU setFrame:CGRectMake(x, 0, 320, 460)];
}
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
label.text=[array objectAtIndex:[indexPath row]];
[self slideView];
}
the running image is as follows:
you can use requireGestureRecognizerToFail: to able to recognize two or more gesture recognizers on same view see example code here
edit
so you can refer this one
the method to be referenced is - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
refer this question for more
//add gesture to mapview-for single touch
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(SLideTheView:)];
[self.view addGestureRecognizer:tap];
tap.numberOfTapsRequired = 1;
//add another gesture to terminate first one
UITapGestureRecognizer *secondGest= [[UITapGestureRecognizer alloc] init];
[tableview addGestureRecognizer:secondGest];
[tap requireGestureRecognizerToFail:secondGest];
[secondGest release];
[tap release];

Why does my UITableView not respond to touchesBegan?

I am using this method
- (void)tableView:(UITableView *)tableView touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
if ([myPickerView isFirstResponder] && [touch view] != myPickerView) {
[myPickerView resignFirstResponder];
}
[super touchesBegan:touches withEvent:event];
}
but my tableView does not respond to touches (applied to the view works, but this is covered by the tableView!)
If this is not possible - is there any other possibility to capture "out of window" touches?
There is no such delegate method as tableView:touchesBegan:withEvent:. If you want to override -touchesBegan:withEvent: for your UITableView, you will need to subclass UITableView. Most problems like this are often better implemented with a UIGestureRecognizer. In the above, I'd probably use a UITapGestureRecognizer.
I found the simplest way to do this is to add a gesture recognizer to the UITableViewController's view.
I put this code in the UITableViewController's viewDidLoad:
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self.view addGestureRecognizer:tap];
And implemented the event handler:
- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
// your code goes here...
}
EDIT:
You could also add the gesture recognizer to the tableview, just change [self.view addGestureRecognizer:tap]; to [self.tableView addGestureRecognizer:tap];
Here is a UITableView subclass solution that worked for me. Make a subclass of UITableView and override hitTest:withEvent: as below:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
static UIEvent *e = nil;
if (e != nil && e == event) {
e = nil;
return [super hitTest:point withEvent:event];
}
e = event;
if (event.type == UIEventTypeTouches) {
NSSet *touches = [event touchesForView:self];
UITouch *touch = [touches anyObject];
if (touch.phase == UITouchPhaseBegan) {
NSLog(#"Touches began");
}
}
return [super hitTest:point withEvent:event];
}

iPhone SDK - How to take a picture with custom camera view?

I want to release the shutter as soon as the user tabs on the screen. I have working code for displaying the camera in fullscreen mode. How can I trigger the shutter by a touch?
- (IBAction) takePicture
{
if (!self.imgPicker) {
self.imgPicker = [[UIImagePickerController alloc] init];
self.imgPicker.allowsEditing = NO;
self.imgPicker.delegate = self;
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
self.imgPicker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.imgPicker.showsCameraControls = NO;
self.imgPicker.wantsFullScreenLayout = YES;
CGAffineTransform cameraTransform = CGAffineTransformMakeScale(1.132, 1.132);
self.imgPicker.cameraViewTransform = cameraTransform;
UIView *headsUpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 480, 320)];
[self.imgPicker setCameraOverlayView:headsUpView];
} else {
NSLog(#"Camera not available.");
self.imgPicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
[self presentModalViewController:self.imgPicker animated:YES];
}
You should use the takePicutre method and then detect any touches on the screen.
http://developer.apple.com/iphone/library/documentation/UIKit/Reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html#//apple_ref/occ/instm/UIImagePickerController/takePicture
Something like this
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
self.imgPicker.takePicture;
}