I'm trying to make an iPhone app that is controlled by touch. I also want a powerup to be activated when the user double-taps. Here's what I have so far:
UITapGestureRecognizer *powerRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(usePower)];
powerRecognizer.delaysTouchesEnded = NO;
powerRecognizer.numberOfTapsRequired = 2;
powerRecognizer.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:powerRecognizer];
[powerRecognizer release];
But the problem is, when I double-tap, my touchesEnded:withEvent: method only fires once, but my touchesBegan:withEvent: method fires twice. Since touchesBegan: sets a timer and touchesEnded: invalidates it, then when touchesEnded: only fires once, the timer is still running. How can I fix this?
In Swift, you can avoid the standard behaviour and let the UITouch event propagate to view and its child, even if a gesture is recognized, with
recognizer.cancelsTouchesInView = false
Both touchesBegan and touchesEnded will be called.
Here is my solution for detecting double-taps:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
if([touch tapCount] == 2) {
// do sth
}
}
Adding a gesture recognizer to a view changes the behavior of several touch handling methods, including touchesEnded:WithEvent:.
From the above link:
After observation, the delivery of
touch objects to the attached view, or
their disposition otherwise, is
affected by the cancelsTouchesInView,
delaysTouchesBegan, and
delaysTouchesEnded properties.
Related
I have an image view. i detected touch in image view like this
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
int viewTag=[touch view].tag;
if ([[touch view] isKindOfClass:[UIImageView class]])
{
//My code
}
}
and touches moved on image view. whenever my touch moved out off the image view in that particular time i need one alert view. how to detect that touch over from the image view in touches moving?...
I recommend using a UIPanGestureRecognizer and adding it to a larger super-view of the image-view you want to detect on. That way even if the touch starts outside and moves into and out of your image-view you can follow the movement of the touch in your gesture handler.
It's pretty easy, make a method called handlePan: for example, create the gesture recognizer using your handler method, add it to the appropriate super-view. Now whenever the gesture is active and the touch moves your handler method will get called and you can check to see if it is inside your image view.
You should use this method...
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
int viewTag=[touch view].tag;
if ([[touch view] isKindOfClass:[UIImageView class]])
{
//My code
}
else
{
//show the alertView here
}
}
and to check that the initial click was on the imageView you have to set a flag in the touchesBegan method... and check it accordingly in the touchesMoved method
You may add a transparent UIButton of the same size on top of the UIImageViewand track UIControlEventTouchDragOutside
[button addTarget:self action:#selector(draggedOutside:) forControlEvents:UIControlEventTouchDragExit];
How can I make it so that while the user is playing on the joystick to move the character at the centre they can also touch the bottom right of the screen (the second touch) to fire the gun? I have looked at other questions but I still cant figure it out...
this is basically the code....:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//make the touch point...
UITouch *touch = [[event allTouches]anyObject];
CGPoint point = [touch locationInView:touch.view];
if (//touching a certain area (on the joystick)) {
//do stuff
}
else if (point.x > 300 && point.y > 200) {
/fire method
}
}
so basically how do I call touchesBegan again to find the position of CGPoint point, and work out if the fire method should be called?
Thanks
EDIT:
I have tried to do that 2nd option and done this:
in view controller.h I added:
#interface fireView: UIView
#end
and in .m I added:
#implementation fireView -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"hi");
}
#end
....but it doesn't log/print "hi"?
Use 2 UIGestureRecognizers. You can create 2 invisible views of needed size - one for joystick and one for fire button. For every view use single gesture recognizer. And then you will be able to handle taps on these view by in different methods without checking if it was fire or joystick.
Let's think you have 2 views - joystickView and fireView already. Then do it like
UITapGestureRecognizer* fireTapGestureRec= [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(fireTapped:)];
fireTapGestureRec.delegate = self;
fireTapGestureRec.numberOfTapsRequired = 1;
fireTapGestureRec.numberOfTouchesRequired = 1;
[fireView addGestureRecognizer:fireTapGestureRec];
[fireTapGestureRec release];
and write fireTapped: to handle the event. And the same thing for joystick.
EDIT
Second option (suggested in comments)
Make subclasses of UIView, like
#interface fireView: UIView
#interface joystickView: UIView
and for each subclass write it own touchesBegan: or touchesEnded:
Im talking about two separate touches on the screen with the same finger
I think i have the coding right for this. but it is almost impossible for me to actually generate a double tap on my iPad. Is it possible to increase the time interval between a single and double tap, so it is easier to trigger. I do a double tap very fast and it captures it as two single clicks. only sometimes am i lucky enough to trigger a double tap. I place my subclass of UIButton item into a scrollview.
Anyway my subclass of UIButton implements:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
NSLog(#"Touch count:%d",touch.tapCount);
if (touch.tapCount == 1)
{
//Do things for one touch
}
else if (touch.tapCount == 2)
{
//Do things for double touch
}
}
This is capturing the event. and that is why i think my code is right, i just couldnt find anything that has to do with UIEvent and what determines how many touches happen. I tested this same thing in a different UIView and it worked exactly as expected.
Your code is detecting two fingers in the view at the same time, not a rapid sequence of on finger (double tap).
If you want to detect a double tap, the easiest way is to use a Gesture Recoginizer. Wherever do setup of your sublass, add this code:
UITapGestureRecognizer *singleFingerDoubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleFingerDoubleTap:)];
singleFingerDoubleTap.numberOfTouchesRequired = 1;
singleFingerDoubleTap.numberOfTapsRequired = 2;
[self addGestureRecognizer:singleFingerDTap];
[singleFingerDTap release];
And implement a method to hande the double tap:
-(void)handleSingleFingerDoubleTap:(id)sender {
//Do stuff
}
you not calling [super touchesBegan:touches withEvent:event]
I have 4-5 uibuttons(also can use uiview instead of button) side by side next to each other.
When user moves touch from one button to other(outside of self view), i want to cancel this touch and start touch for the next button(view).
How to achieve this functionality?
I used uiview instead of buttons kept it next to each other, handled its touches events.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject]; //anyobject
CGPoint touchLocation = [touch locationInView:touch.view];
if(![self pointInside:touchLocation withEvent:nil])
{
//touch is outside of my current view , do something to cancel current touch and i can get touchesmoved for the next view . help me to write something.
}
}
Is there any other way to handle event when we have such UI.
Thanks.
If you are using UIButtons, you can just use the button's events to do this. When you get a touchDown or touchDragInside, then play your note. Stop when you get a touchUpInside or a touchDragOutside.
You need to handle the touches yourself from a UIWindows subclass, or from the UIViewController container (in this case remember to disable the userInteractionEnabled property)
I have a view derived from a UIScrollView controller that I use to page through images in a library. That works fine.
I overlayed a UIView on the right side to handle touches for scrolling quickly through the list of images. My problem is that if I pass the touches through to either 'super' or 'nextResponder' they never make it to the UIScrollView object below.
My question is how can I force the UIScrollView below to handle the touches that I don't need to handle? I'm setting a timer for 0.3 seconds that during that time all touches are passed to the UIScrollView to handle. So if the user started a swipe gesture to turn the page, it will happen.
Here's the code for the touchesBegan method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
// Start a timer to trigger the display of the slider and the processing of events.
touchesTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(showSlider:) userInfo:nil repeats:NO];
// Where are we at right now?
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
lastPage = [self page:currentPoint.y];
// Pass the event though until we need it.
// [self.nextResponder touchesBegan:touches withEvent:event];
if ([touch.view isKindOfClass:[UIScrollView class]])
{
if (self.nextResponder != nil &&
[self.nextResponder respondsToSelector:#selector(touchesEnded:withEvent:)])
{
[self.nextResponder touchesEnded:touches withEvent:event];
}
}
}
I would try using a regular UIViewController rather than a UIScrollViewController and then add the scroll view object where you need it and put the scroll faster view you want on the right side. This way they are separate and the touches won't get confused.
Here's an easier solutions that worked well for me:
In the UIView on top of the UIScrollview make the width and height both 0 (so that the view is no longer technically on top of the UIScrollView) and set clipsToBounds = NO (so that the contents of the view still show up on top of the UIScrollView).
self.OverlayView.clipsToBounds = NO;
CGRect frame = self.OverlayView.frame;
self.OverlayView.frame = CGRectMake(frame.origin.x, frame.origin.y, 0, 0);
Note that if the UIView contains interactive controls then they will no longer work. You'll need to move it into it's own view above the UIScrollView.