iOS: UIVIew touches delegates vs single tap recognizer - ios

I am handling Touches delegates and UITapGestureRecognizer for single tap. Following are the methods. When I single tap on my View, I get three events in order of TouchesBegan, SingleTap, and TouchesEnded. Is there anyway to stop TouchesEnded event when I have SingleTap detected? Basicaaly I don't want to perform tasks in TouchesEnded if TouchesMoved didn't happen. I have done this using failTouchEnded BOOL variable in SingleTap and checking it in TouchesEnded but not sure if it is a good idea!
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
failTouchEnded = NO;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(failTouchEnded) return;
}
- (void)GestureView:(GestureView*)view handleSignleTap:(UITapGestureRecognizer*)recognizer {
failTouchEnded = YES;
}

gestureRecognizer has a property cancelsTouchesInView. if it is YES (which is default) cancels the touches if the gesture recognizer recognizes the gesture. So you will get a touches canceled instead of touches ended. You should set delaysTouchesEnded to YES too if it is not YES

Related

how to stop touchesEnded: withEvent: propogate from subView to ViewController?

I have a view being added as a subView of a viewController.
Both this subview and viewController all implement this method
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
I found that when I tapped the subView, both these method will be called.
I want to call subview's touchesEnded only. How to achieve this nicely? (not to add a gesture in it)
In the touchesEnded, apple doc says this "If you override this method without calling super (a common use pattern), you must also override the other methods for handling touch events, if only as stub (empty) implementations."
what's the other method ?
You are close!
To prevent to pass touch event to superview, you should override all the methods for the touch events. Add all the touch event methods to your subview, then you should be OK.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
Instead of implementing the subview's touchesDidEnd why not do the whole work in the superview's touchesDidEnd something like this
if([touch anyObject].view == subview){
return;
}
This way you will be able to know whether the touch was originally from the subview or the superview.
Alternative : You can implement both the methods in the superview and subview, but like the above code you can return the call in the superview, if the view it interacted with is the subview, and keep working in the touches code in the subview
If in case you want a different opinion plz let us know what you are trying so that we can give the answer accordingly

Touches Ended not called when subclassed with UIGestureRecogniser

I am trying to create a custom gesture recogniser for my view. I am following this answer mentioned in here: But for some reason the touched Ended and also touches movied are not getting called. Only touches began get called.
SubClass:
#import "TapGesture.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation TapGesture
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.state == UIGestureRecognizerStatePossible) {
self.state = UIGestureRecognizerStateRecognized;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
}
#end
and I am initialising the TapGesture as follows:
TapGesture *tapGesture=[[TapGesture alloc]initWithTarget:self action:#selector(incrementValue)];
tapGesture.delaysTouchesEnded=NO;
tapGesture.cancelsTouchesInView=NO;
[_workButton addGestureRecognizer:tapGesture]; //_workButton is a UIView
I don;t have any other gesture recognisers in the view. If I use the same methods in the UIView, all of them are called as expected.
Why the touchesEnded/touchesMoved are not getting called when overrride in UIGestureRecogniser class ?
When subclassing UIGestureRecognizer you must make it act like a continuous gesture and handle it's state machine by yourself (i.e., manually set it's state).
From the iOS Developer Library Docs on UIGestureRecognizer:
Subclasses must set the state property to the appropriate value when they transition between states.
See here for more info (Scroll down to Subclasing Notes)
Note: to make state read/write and not read-only, you should use UIGestureRecognizerSubclass.h as also noted on the docs:
The state property is declared in UIGestureRecognizer.h as being read-only. This property declaration is intended for clients of gesture recognizers. Subclasses of UIGestureRecognizer must import UIGestureRecognizerSubclass.h. This header file contains a redeclaration of state that makes it read-write.
I found that this is required for a double tap gesture but not for a single tap gesture:
doubleTapGestureRecognizer.delaysTouchesEnded = NO;

iOS delay touchUpInside button event

I have some custom animations in my UIButton subclass. I override some inner methods for starting and ending animation
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
//there is my start animation
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
//there is my end animation
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
//there is my end animation
}
There is almost all good. All my buttons make some actions on event UIControlEventTouchUpInside. And there is my problem. Methods touchesEnded and touchesCancelled are called only after my app handle UIControlEventTouchUpInside event. So animations in button looks not so nice like I want.
So question is: can I delay UIControlEventTouchUpInside event on some ms and firstly handle touchesEnded method of my button for smooth animation?

Programmatically initiate hitTest:WithEvent after gesture recognizer was canceled and return a different UIView to respond to events

I am overriding hitTest:withEvent to return self (the bottom most view)-
When returning self - my view will respond to touch events in turn initiating gesture recognizers.
If a gesture is canceled or some set of conditions happened - I want to manually initiate hitTest:withEvent and then return a different view to take care of the same sequence of events/touches that occurred. This is necessary as a gesture recognizer only initiates after hitTest:withEvent returns the gestures view and its state changed to began.
I am not sure how to do this - I thought about manually calling on my subviews
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}
But I don't have the event parameter (The gesture received it)
I think this could not be done, pass touch event to UIGestureRecognizer is private API. But you can pass touch event the bottom most view received to any view you like and do your own gesture recognize.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView* selectView = [self _findMatchView];
// maybe convert touches to selectView coordinate
[selectView handleTouchBegan:touches withEvent:event];
}

iPhone - Detecting touches and canceling them?

I am dtecting touches on my UIView.
Is some situations I want to be abel to cancel touches so that touchesEnded won't get called. But doesn't matter what touchesEnded will always get called?
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (SOMETHING_SPECIAL)
{
[super touchesCancelled:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//I don't want it to get here if touches were canceled how can i do this?
}
- In my touchesEnded how can I determine whether touches were canceled or not?
TouchesEnded will always get called wherever your touches where canceled or not, so I would suggest instead having that exact:
if (SOMETHING_SPECIAL)
{
}
In your:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
To get the touchesCancelled event, implement this method from UIResponder.
touchesCancelled:withEvent:.

Resources