UIGestureRecognizer Method Keeps Getting Called - ios

I am trying use various NSTimers in conjunction with a pan gesture, firing them only when the translation in view reaches a certain point in the view, and invalidating the timers when they go beyond a certain point. However, I found that even if I fire the timers within the .Changed state, the gesture method itself is called continuously as the user pans. As such, the NSTimer is fired continuously and is not working as it should. Is the only option to move the NSTimers outside the pan gesture? Or is there another solution? Thanks.

the gesture method itself is called continuously as the user pans
This is correct behavior. It is up to you to deal with the gesture recognizer action method being called many times. You can and should distinguish why the action method is being called, by examining the gesture recognizer's state. It will be called once for the Begin state, many times for the Changed state, and one last time for the Ended state. Any gesture recognizer action method must take account of this and structure itself accordingly, usually as one big switch statement.

Related

Pass tap events to superview but handle long press

I'm trying to pass tap events to the superview but handle longpress events. I've added LongPressGestureRecognizer to the top view but the tap events aren't passed to the superview. I tried multiple approaches:
Overriding hitTest doesn't work since the longpress gesture recognizer handler doesn't get called
isUserInteractionEnabled - same as above
Overriding touchesBegan/Ended and calling them manually on the superview doesn't trigger the tap event
Handing complex tap interactions can be hard, and mixing different approaches can make it much much harder.
Generally, the best way to handle it is to have a single view that has multiple gesture recognisers on them. Implement the UIGestureRecognizerDelegate method gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) and gestureRecognizer(_:shouldRequireFailureOf:) to handle conflicts. When a touch event is recognised it can delegate the action to whatever other object needs to deal with it. Having different views all trying to deal with touches at the same time is not a good way to deal with the problem. Gestures are dependent on other gestures and cannot all be handled independently by different views.

UILongPressGestureRecognizer only checks for numberOfTouchesRequired at initial touch

I have an app that has three long press gestures for 1,2, and 3 touches.
An Issue: through testing, my success rate of getting 2 or 3 touches to register correctly at the start of gesture recognition was far less than 100%. In further testing, it appears that the UILongPressGestureRecognizer [LPGR] only checks for the number of touches at the start and fails instantly if the number of touches is not what it's expecting.
My (potential) solution: I have started building a generic UIGestureRecognizer that will check for the number of touches at the end of a time interval, then pass touches to whatever branch of code that handles what was 1,2,and 3 touches.
My Question: Is there a better way? And barring that, is there a way to use the code already in an existing gestureRecognizers? I have not been able to actually change the state through reference (someRecognizer.state = .changed does not seem to do anything)

Accurate start position for UIPanGestureRecognizer?

I am using a UIPanGestureRecogniser to implement drag and drop. When the drag starts I need to identify the object that is being dragged. However the objects are relatively small. And if the user doesn't hit the object right in the centre of the object it isn't getting dragged.
The problem is that when the gesture handler is first called with the state UIGestureRecognizerStateBegan, the finger has already moved several pixels, and so [UIPanGestureRecognizer locationInView:] returns that point, which is not where the gesture truly started. That makes sense as it can only recognize a pan after a few pixels of movement. However, I need the absolute start of the gesture, not the position after the gesture has first been recognized.
I'm thinking that maybe I need to implement a tap gesture recognizer as well, purely to capture the first touch. But that seems like a hack for what is not an unusual requirement. Is there no other way of getting that first touch from within the pan gesture recognizer?
UIGestureRecognizerDelegate protocol provides methods gestureRecognizerShouldBegin: and gestureRecognizer:shouldReceiveTouch: that can help you evaluate the touches before the pan has transitioned to state UIPanGestureRecognizerStateBegan

How to differentiate between user swipe and tap action?

I am developing a app in which I have a view which contains subView in it.
I want to track both swipe and tap actions such as a single click.
Actions should be tracked only when the user touches within my subview. When the user taps I want to perform one action, when the user swipes I want perform another.
For tracking the swipe, I implemented UIGestureRecognizer and it is working fine. But I don't know how to track the tap option. Please guide me how to achieve this.
The main thing is, when I tap it should call tap action only and vice versa.
You can use UITapGestureRecognizer for tap gestures.
"UITapGestureRecognizer is a concrete subclass of UIGestureRecognizer
that looks for single or multiple taps. For the gesture to be
recognized, the specified number of fingers must tap the view a
specified number of times."
This method includes the numberOfTapsRequired ("The number of taps for the gesture to be recognized.") and numberOfTouchesRequired ("The number of fingers required to tap for the gesture to be recognized") properties where you can set exactly how you want it to react to user action.
In this case, as you only want it to be activated when tapped once, the default settings for both these properties (both have default values of 1) should be fine.
The best place to get the information is Defining How Gesture Recognizers Interact of Event Handling Guide for iOS
When a view has multiple gesture recognizers attached to it, you may
want to alter how the competing gesture recognizers receive and
analyze touch events. By default, there is no set order for which
gesture recognizers receive a touch first, and for this reason touches
can be passed to gesture recognizers in a different order each time.
You can override this default behavior to:
Specify that one gesture recognizer should analyze a touch before another gesture recognizer.
Allow two gesture recognizers to operate simultaneously.
Prevent a gesture recognizer from analyzing a touch.

Touch Events touchesCancelled and touchesEnded

I have a project that I started out using a tap gesture recognizer for. I realized I didn't have enough control with the tap gesture recognizer, so I've started coding with using my viewcontroller as a UIGestureRecognizerDelegate. Just to make sure I was on the right track, I added methods for touchesBegan, touchesMoved, touchesEnded, touchesCancelled. The methods are empty except for NSLog calls so I can tell what is being fired when I try different things.
Things worked as expected except that I was getting a bunch of calls to touchesCancelled. I assume this is because of the tap gesture recognizer I still have in place. I'm not ready to remove the tap gesture recognizer, so I just wanted to confirm that this is what would happen if a gesture I used was actually a tap.
The documentation says:
This method is invoked when the Cocoa Touch framework receives a
system interruption requiring cancellation of the touch event; for
this, it generates a UITouch object with a phase of
UITouchPhaseCancel. The interruption is something that might cause the
application to be no longer active or the view to be removed from the
window When an object receives a touchesCancelled:withEvent: message
it should clean up any state information that was established in its
touchesBegan:withEvent: implementation.
But I suspect my scenario just outlined is just as likely. Am I correct?

Resources