Help with touchesMoved when dragging fingers - iphone

I'm implementing touchesMoved, touchesBegan, and touchesEnded on a few UIButtons, so that I can slide my fingers over them and have them call the appropriate actions.
It seems to be working almost as intended, however, if I press two fingers outside of the two buttons' frames, and then slide them into the buttons' frames at the same time, the function within touchesMoved gets called multiple times. Instead, it should only call each button's function once while in the button's frame.
Below is my code.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(Button1.frame, location))
{
if (!Button1.isHighlighted){
if(!button1Highlighted) {
[self doAction1];
}
[Button1 setHighlighted:YES];
button1Highlighted = YES;
}
}
else {
[Button1 setHighlighted:NO];
button1Highlighted = NO;
}
if(CGRectContainsPoint(Button2.frame, location))
{
if (!Button2.isHighlighted){
if(!button2Highlighted) {
[self doAction2];
}
[Button2 setHighlighted:YES];
button2Highlighted = YES;
}
}
else {
[Button2 setHighlighted:NO];
button2Highlighted = NO;
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(Button1.frame, location))
{
[Button1 setHighlighted:YES];
button1Highlighted = YES;
[self doAction1];
}
if(CGRectContainsPoint(Button2.frame, location))
{
[Button2 setHighlighted:YES];
button2Highlighted = YES;
[self doAction2];
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(Button1.frame, location))
{
[Button1 setHighlighted:NO];
button1Highlighted = NO;
}
if(CGRectContainsPoint(Button2.frame, location))
{
[Button2 setHighlighted:NO];
Button2Highlighted = NO;
}
}
}
Any help is greatly appeciated. Thanks!

If -touchesMoved: is getting called with multiple touches such that there's one touch on Button1 and another on Button2 and both buttons are not highlighted, then the touch over Button1 will highlight Button1 and unhighlight Button2. Meanwhile, in the same loop in the same call to -touchesMoved:, the touch over Button2 will essentially reset Button1's highlight state back to unhighlighted.
-touchesMoved: will be called as long as there are touches, and each call will cycle the two buttons again.
Maybe you need to add a 'hasBeenHighlighted' property to your buttons. I am not sure how best to initialize this property to NO for all your buttons. But it would need to be set to YES inside -setHighlighted: and it would need to be checked before calling -doActionX.
I am not sure that I understand exactly what you are trying to achieve, but I hope this is of some help.

I ended up getting it to work by storing the number of touches on the screen in a variable called touchesCount. I then increment it in touchesBegan, and decrease it in touchesEnded. Then in the touchesMoved before calling doActionX, I checked to make sure touchesCount < 2.

Related

iOS : detect UIImageView for UITouch Events

I've added some UIImageView dynamically and filled it with different images, what I am trying to do is "Allow user to set position for any UIImageView", for that I used
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//Here I want the object of particular UIImageView on which user touched.
}
In that method I'm doing,
NSLog(#"%#",[touches anyObject]);
It returns output
<UITouch: 0x68b95e0> phase: Began tap count: 1 window: <UIWindow: 0x68875d0; frame = (0 0; 320 480); layer = <UIWindowLayer: 0x68b6470>> view: <UIImageView: 0x6a74cf0; frame = (83.7763 83.7763; 182.447 182.447); transform = [0.968912, -0.247404, 0.247404, 0.968912, 0, 0]; alpha = 0.8; opaque = NO; tag = 3; layer = <CALayer: 0x6a74980>> location in window: {161, 230} previous location in window: {161, 230} location in view: {52.7761, 105.448} previous location in view: {52.7761, 105.448}
Note, in above output, it showing my UIImageView object on which I touched. But I want that object from it!!!
I want my UIImageView on which user touched?, I have already set property userInteractionEnabled=YES so the problem isn't with it!
I used below code to get it so, but it wont work.
NSInteger tag=[[[touches anyObject] view] tag]; //It only returns tag of UIView tag
I Google it but doesn't come with solution!
Thank you in advance for any help!
Here you go:
this is only for one imageview you can detect the other by the same if statement.
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touch_point = [touch locationInView:self.view];
if (![imageView pointInside:touch_point withEvent:event])
{
NSLog(#"point inside imageview");
}
}
or you can also do this :p
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
if (touch.view == iv)
{
NSLog(#"i got you");
}
}
like this: (iv and iv2 are 2 different UIImageView`s)
if (touch.view == iv)
{
NSLog(#"i got you");
}
if (touch.view == iv2)
{
NSLog(#"i got you too :p");
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[touches anyObject];
if([[touch valueForKey:#"view"] isKindOfClass:[UIImageView class]])
{
UIImageView *viewSelected=(UIImageView *)[touch valueForKey:#"view"]; //it returns touched object
//for further differences can user viewSelected.tag
}
}
Code to get only the X and Y coords from the UIImageView:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touch_point = [touch locationInView:self.imgView];
if ([imgView pointInside:touch_point withEvent:event])
{
NSLog(#"point inside imageview");
cords=[NSString stringWithFormat:#"%f,%f",touch_point.x,touch_point.y];
NSLog(#"cords are%#",cords);
}
}
You can use UIButton with Image properties instead of UIImageView.
If you would like to call your own function ,It's pretty easy to handle many event like Touch up inside or Touch Cancel by adding selector.
UIButton *yourBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
/* Depend on your dynamic programming handle */
yourBtn.frame = CGRectMake(40, 140, 240, 30);
/* If you prefer just only image ,no need to set Title name */
[yourBtn setTitle:#"Your Button Title" forState:UIControlStateNormal];
/* choose yourFunction for each UIButton */
[yourBtn addTarget:self action:#selector(yourFunction) forControlEvents:UIControlEventTouchUpInside];
/* your button will be appear in the position that you have define */
[self.view addSubview:yourBtn];
Hope It helps you!
1 Subclass UIImageView and implement:
Responding to Touch Events
– touchesBegan:withEvent:
– touchesMoved:withEvent:
– touchesEnded:withEvent:
– touchesCancelled:withEvent:
Responding to Motion Events
– motionBegan:withEvent:
– motionEnded:withEvent:
– motionCancelled:withEvent:
or:
2 Add UIGestureRecognizer to each UIImageView.
You could add the View that you add into an NSMutableArray and then just compare like this:
I am not in my mac, but It's something similar to this:
NSInteger viewID = [_views indexOfObject:[[touches anyObject] view]];
This return and number if not isn't exist do this:
if (viewID != NSNotFound) {
//it exist the view and its in the array
}

Weird issue with touchesMoved with multiple touches

I have code in place which lets you tap buttons and play a sound for each button, as well as sliding your fingers across the buttons and having each button play it's corresponding sound as you enter the button's area. It's working pretty good, except for a strange bug:
If you press your fingers on two buttons at the same time it will play both sounds initially. However, if you continue leaving your fingers on the button, and then release your fingers off the screen while moving one of them ever so slightly (still within the confines of each button), it will play one of the sounds of the two buttons. How can I prevent this from happening? I only want the sounds to play if you're sliding a finger across the buttons or hitting them initially, not if you're just moving them slightly while hitting two buttons at the same time and then releasing your finger.
Below is my code:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouches = [event allTouches];
touchesCount = [allTouches count];
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(soundButton1.frame, location))
{
if (!soundButton1.isHighlighted && touchesCount < 2){
soundID = #"0";
[self playSound];
[soundButton1 setHighlighted:YES];
}
}
else {
[soundButton1 setHighlighted:NO];
}
if(CGRectContainsPoint(soundButton2.frame, location))
{
if (!soundButton2.isHighlighted && touchesCount < 2){
soundID = #"1";
[self playSound];
[soundButton2 setHighlighted:YES];
}
}
else {
[soundButton2 setHighlighted:NO];
}
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(soundButton1.frame, location))
{
[soundButton1 setHighlighted:YES];
soundID = #"0";
[self playSound];
}
if(CGRectContainsPoint(soundButton2.frame, location))
{
[soundButton2 setHighlighted:YES];
soundID = #"1";
[self playSound];
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *t in touches) {
CGPoint location = [t locationInView:t.view];
if(CGRectContainsPoint(soundButton1.frame, location))
{
[soundButton1 setHighlighted:NO];
}
if(CGRectContainsPoint(soundButton2.frame, location))
{
[soundButton2 setHighlighted:NO];
}
}
}
Try disabling the sounds for a short (very) period. In touchesEnded:withEvent:, check whether it has two touches, then do something like this,
soundDisabled = YES;
You can either request a selector to run after delay
[self performSelector:#selector(enableSound) withObject:nil afterDelay:0.2];
and then
- (void)enableSound {
soundDisabled = NO;
}
Or you could wait for the second finger to go up and then
soundDisabled = NO;

Drag and drop objects and return them to their original position

I'm trying to drag an image and let it return to it's original position after releasing it. So far, I can drag an image by creating it as a button using the following code: (as seen in the answer to this question: Basic Drag and Drop in iOS )
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:#selector(imageTouch:withEvent:) forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button setImage:[UIImage imageNamed:#"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];
And later I define:
- (IBAction) imageMoved:(id) sender withEvent:(UIEvent *) event
{
CGPoint point = [[[event allTouches] anyObject] locationInView:self.view];
UIControl *control = sender;
control.center = point;
}
How can I make it return to it's original position after releasing it ?, for starters I will save the position at which each image is supposed to return, but then, there is anyway to indicate an UIImage to go from it's current position and move to another one ?, or any other alterative solution ?
UIGestureRecognizer is recommended by Apple for recent iOSes. You can use it not only for UIButton, but also for UIView (especially UIImageView in your case), etc. So I would like to recommend it.
In the interface:
#interface TestDragViewController : UIViewController {
IBOutlet UIImageView *dragImage;
CGPoint originalCenter;
}
In viewDidLoad, plz remember to enable userInteraction:
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(dragGesture:)];
[dragImage addGestureRecognizer:panGesture];
[dragImage setUserInteractionEnabled:YES];
}
And in its selector, I just set animation to visualize the returning effect:
#pragma mark -
#pragma mark UIPanGestureRecognizer selector
- (void) dragGesture:(UIPanGestureRecognizer *) panGesture{
CGPoint translation = [panGesture translationInView:self.view];
switch (panGesture.state) {
case UIGestureRecognizerStateBegan:{
originalCenter = dragImage.center;
}
break;
case UIGestureRecognizerStateChanged:{
dragImage.center = CGPointMake(dragImage.center.x + translation.x,
dragImage.center.y + translation.y);
}
break;
case UIGestureRecognizerStateEnded:{
[UIView animateWithDuration:kImageReturnTime
animations:^{
dragImage.center = originalCenter;
}
completion:^(BOOL finished){
NSLog(#"Returned");
}];
}
break;
default:
break;
}
[panGesture setTranslation:CGPointZero inView:self.view];
}
Cheers,
Tommy
Define CGPoint Globally, or in .h file.
CGPoint point;
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:self action:#selector(imageTouch:withEvent:)forControlEvents:UIControlEventTouchDown];
[button addTarget:self action:#selector(imageMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
[button setImage:[UIImage imageNamed:#"vehicle.png"] forState:UIControlStateNormal];
[self.view addSubview:button];
And later I define:
- (IBAction) imageMoved:(UIButton*) sender withEvent:(UIEvent *) event
{
point = [[[event allTouches] anyObject] locationInView:self.view];
UIControl *control = sender;
control.center = point;
[sender addTarget:self action:#selector(touches:withEvent:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)touches:(UIButton *)button withEvent:(UIEvent *)event {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction animations:^{
button.center = point;
} completion:^(BOOL finished){
if (finished) {
// do anything yu want
}
it works awesome cause i am using it in my project!
How about calling a selector on UIControlEventTouchUpInside and then in the selector, just set the frame of your button to a new x and y coordinate (the ones that you have saved).
In the solution I linked to in the thread you are referring I have created a generic drag drop manager that takes care of the return of a dragged object to the original position (if released outside a known dropzone). Currently there is no animation in the solution, but you could combine some of the ideas presented above.
See the some how generic drag and drop solution in iOS
You know what, I was just working on this yesterday, so here you go with a little bonus animation:
- (void)previewImageTouchUpInside:(UIButton*)aButton {
if (!previewImageDragged) {
[self selectPreviewImage:aButton];
return;
}
previewImageDragged = NO;
// If user drag photo a little and then release, we'll still treat it as a tap
// instead of drag
if ((originalPositionOfButton.x - aButton.center.x) *
(originalPositionOfButton.x - aButton.center.x) +
(originalPositionOfButton.y - aButton.center.y) *
(originalPositionOfButton.y - aButton.center.y) < 10) {
aButton.center = originalPositionOfButton;
[self selectPreviewImage:aButton];
return;
}
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionCurveEaseOut |
UIViewAnimationOptionAllowUserInteraction animations:^{
aButton.center = originalPositionOfButton;
} completion:nil];
}
- (void)previewImageDraggedInside:(UIButton*)aButton event:(UIEvent*)event {
if (!previewImageDragged) {
originalPositionOfButton = aButton.center;
[self.view bringSubviewToFront:aButton];
[self.view bringSubviewToFront:[self.view viewWithTag:CHOOSE_PHOTO_BUTTON_TAG]];
}
UITouch* touch = [[event allTouches] anyObject];
previewImageDragged = YES;
aButton.center = CGPointMake(aButton.center.x+[touch locationInView:self.view].x-
[touch previousLocationInView:self.view].x,
aButton.center.y+[touch locationInView:self.view].y-
[touch previousLocationInView:self.view].y);
}
It still has a few magic numbers and I haven't renamed the functions and variables to suit your example, but it should be clear. I also did the indentation here since I don't manually break long lines in my code.

UIButton grid activate same time dragging

I have a grid of various UIButtons (5 x 5)... Now I have a UIControlEventTouchUpInside.. this means that when the user wants to choose various buttons need to press each, one by one...
How can I do to activate the buttons when the user is dragging his finger over various buttons.
Here is the code I use:
for (i = 0; i < num_caselles; i++)
{
lletra = [[UIButton alloc] initWithFrame:CGRectMake(POS_H, POS_V, mida_boto, mida_boto)];
[botones addObject: lletra];
[lletra setTitle: [caselles objectAtIndex: i] forState: UIControlStateNormal];
lletra.tag = i;
[lletra addTarget:self action:#selector(lletraPitjada:) forControlEvents: UIControlEventTouchUpInside];
}
You can also react to:
UIControlEventTouchDragEnter or
UIControlEventTouchDragExit
to handle these cases.
Ok finally I resolved this way :
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event touchesForView:self] anyObject];
CGPoint location = [touch locationInView:touch.view];
for(UIButton*boton in botones)
{
if(CGRectContainsPoint([boton frame], location) && boton.tag != boton_anterior)
{
boton_anterior = boton.tag;
[self lletraPitjada:boton];
}
}
}
I override/commented the button set action, because is not working for me :
//[lletra addTarget:self action:#selector(lletraPitjada:) forControlEvents: UIControlEventTouchDragEnter];
and deactive user interaction, because UITouch dont like buttons :
lletra.userInteractionEnabled = NO;
And voilà... all is working perfect...

How do you get the touchesBegan coordinates when a UIButton is triggered?

I have a touchesBegan method set up to pick up the coordinates when the user touches the screen. It works great except when I touch down on a UIButton. How do I make it so that the button triggers the touchesBegan method too?
Add event handlers to the button, something like the following,
[myButton addTarget:self action:#selector(dragBegan:withEvent:) forControlEvents: UIControlEventTouchDown];
[myButton addTarget:self action:#selector(dragMoving:withEvent:) forControlEvents: UIControlEventTouchDragInside];
[myButton addTarget:self action:#selector(dragEnded:withEvent:) forControlEvents: UIControlEventTouchUpInside | UIControlEventTouchUpOutside];
Handle the events as the following, you can get the touch point from the event ev as below,
- (void)dragBegan:(UIControl *)c withEvent:ev {
NSLog(#"dragBegan......");
UITouch *touch = [[ev allTouches] anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
NSLog(#"Touch x : %f y : %f", touchPoint.x, touchPoint.y);
}
- (void)dragMoving:(UIControl *)c withEvent:ev {
NSLog(#"dragMoving......");
}
- (void)dragEnded:(UIControl *)c withEvent:ev {
NSLog(#"dragEnded......");
}
This is not my own answer. I got this code from http://sree.cc/iphone/handling-touche-events-for-uibuttons-in-iphone and made some minor changes.
Try overriding UIButton class and in the touchesBegan method call [super touchesBegan..].