I have a UIScreenEdgePanGestureRecognizer (as part of a custom pop gesture) that works 85-90% of the time. There's that 10-15% when it just doesn't fire no matter how perfectly you swipe. The UIScreenEdgePanGestureRecognizer is competing with a UIScrollView that contains the navigation controller which also detects gestures in the same direction (horizontal) so I suspect that may be the issue.
Is this relatively common to have a UIScreenEdgePanGestureRecognizer fire inconsistently, particularly when UIKit has to take its best guess if the gesture is a screen-edge pop or a regular pan? And is there a way to reconfigure UIScreenEdgePanGestureRecognizer to get it working with a higher success rate, perhaps by enlarging the rectangle?
Prevent UIScrollView's UIPanGestureRecognizer from blocking UIScreenEdgePanGestureRecognizer:
[scrollView.panGestureRecognizer requireGestureRecognizerToFail:self.navigationController.interactivePopGestureRecognizer];
This method creates a relationship with another gesture recognizer that delays the receiver’s transition out of UIGestureRecognizerStatePossible.
Related
After rearranging the structure of my UIViews I seem to have introduced a delay in drawing (users can draw on the screen with their finger). Before the onset of drawing was negligible, but now there is a noticeable latency between the initial movement of the finger and the drawing of the line. As I keep drawing the latency seems to disappear. So it is possible the initial touch event is delayed somewhere.
My question is not how to solve this specific instance, but in the diagnosis I ran into the following question: What is the earliest point I can register the (time of onset of the) touch of the screen?
Now I put timestamps in hitTest in all the UIResponders (UIWindow -> UIView -> ... -> DrawingView). But could there be delays before the first call to hitTest in UIWindow?
Thanks!
UIWindow delivers touch events to gesture recognizers before delivering the events directly to views. This happens inside -[UIWindow sendEvent:](https://developer.apple.com/documentation/uikit/uiwindow/1621614-sendevent). I believe it hit-tests the view hierarchy to find the gesture recognizers that might be interested in the event, so you should not expect the hitTest:withEvent: messages to be delayed.
If there is a gesture recognizer on your view or any of its superviews, that gesture recognizer can delay the delivery of touch events.
Note that many of UIKit's standard views use gesture recognizers. In particular, UIScrollView uses gesture recognizers that can delay touch events, and both UITableView and UICollectionView are subclasses of UIScrollView.
My app uses a paged horizontal scroll view. Each page has UIControls which the user can tap.
UIScrollView does a good job of handling cancellation of touches and swipes. If the user starts swiping fast enough, it's always a swipe. If the user touches down long enough to activate the highlighted state, the scroll view doesn't attempt to swipe.
I'm trying to duplicate this behavior with a UIPanGestureRecognizer subclass so that I can respond to downward swipes within my scrollview. However, I can't get the gesture to cancel in the event of the UIControls getting highlighted.
So far I've done the following:
self.refreshGesture.cancelsTouchesInView = YES;
self.refreshGesture.delaysTouchesBegan = NO;
self.refreshGesture.delaysTouchesEnded = NO;
This seems to duplicate the way UIScrollView passes touches to views, but it doesn't duplicate the way that UIScrollView's pan gesture recognizer gets cancelled. self.refreshGesture is always triggered no matter how slowly the user swipes, or what the state of the UIControls are.
I've tried setting the delegate on my gesture, and this may be the way to go. But I haven't found a combination that works. For example, just checking if the touch starts within a UIControl cancels too frequently. I've also tried overriding gestureRecognizerShouldBegin in my controls, but this seems like a hack and has far reaching implications (interferes with UITextView's gestures, for example).
In this GIF, you can see that the control activates on touch, and the scrollview cancels scroll if that happens. But my downward pan gesture is not cancelled in the same manner:
I wasn't able to duplicate this exactly, but there are two possibilities suggested by WWDC 2014 #235.
Add a transparent scrollview over your main content and move its gesture recognizer onto your root view. This is what did. It let me use UIScrollViewDelegate which ended up being sufficient.
Use a "timeout" gesture recognizer. The video suggests requiring the timeout gesture to fail, but in my case it worked better to use a long press gesture and cancel my pan if the long press fired. 0.1 seconds seemed to work better than their suggested 0.15 seconds.
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
I'm new to developing iOS apps,
I've successfully implemented a Swipe Gesture Recognizer,
What I was wondering is if there is an easy to use recognizer like the swipe gesture. That would let you implement the homescreen page turning effect but just on a small view in the view controller?
If your unclear on what effect I mean, when you look at the iPhone's homescreen you can drag your finger and it responds instantly (unlike swipe) and also has some spring feeling to it, is this some effect I can use, or do I manually have to program this into the code if so is there a tutorial that explains this?
Thanks,
I hope my question makes sense.
Have a look at UIPanGestureRecognizer:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIPanGestureRecognizer_Class/Reference/Reference.html
UIPanGestureRecognizer is a concrete subclass of UIGestureRecognizer
that looks for panning (dragging) gestures. The user must be pressing
one or more fingers on a view while they pan it. Clients implementing
the action method for this gesture recognizer can ask it for the
current translation and velocity of the gesture.
A panning gesture is continuous. It begins
(UIGestureRecognizerStateBegan) when the minimum number of fingers
allowed (minimumNumberOfTouches) has moved enough to be considered a
pan. It changes (UIGestureRecognizerStateChanged) when a finger moves
while at least the minimum number of fingers are pressed down. It ends
(UIGestureRecognizerStateEnded) when all fingers are lifted.
Clients of this class can, in their action methods, query the
UIPanGestureRecognizer object for the current translation of the
gesture (translationInView:) and the velocity of the translation
(velocityInView:). They can specify the view whose coordinate system
should be used for the translation and velocity values. Clients may
also reset the translation to a desired value.
Edit: The spring feeling part you would need to implement yourself. Since iOS 7 there is UIDynamics which contains different animators, for what you describe you may need UIGravityBehavior and maybe UICollisionBehaviour. Look at the WWDC 2013 videos for this topic, I think you will find some examples there.
My table view has a text field above it. And whenever the text field is focussed, a swipe gesture is registered. When the swipe gesture is recognized, the keyboard is dismissed. The code is working for all gestures except for swipe up gesture is not working. This is my code
swipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self action:#selector(dismissKeyboard)];
[swipe setDirection:UISwipeGestureRecognizerDirectionUp];
Can someone please let me know if there is any problem?
if all the other gestures works, that means there is no logic problem.
check out spelling errors.
and reapply the swipe gesture, and check out everything (outlets etc.).
I don't know about this case, but I know that when I've had gestures on a custom container view and then added a child view with its own gestures, I've had to iterate through the child's gestures and tell them to require my gestures to fail (i.e. mine take precedence). I've done this with scroll views successfully:
for (UIGestureRecognizer *gesture in self.scrollView.gestureRecognizers)
{
[gesture requireGestureRecognizerToFail:myGesture];
}
The only times I've had problems with that are views like UITextView which remove and add gestures as you go in and out of edit mode, so that's a hassle.
Also, while I tried this with standard gestures, I've subsequently shifted to custom gestures that I've programmed to failed as quickly as possible (check the start location and fail immediately if it won't support the direction my gesture requires, rather than waiting for a bunch of touchesMoved to come to the same conclusion). If you don't want to interfere with the child view's gestures, be as aggressive as possible in letting yours fail. Maybe this isn't an issue with a swipe gesture, but it's a possible consideration if you find that your gestures end up changing the behavior of the child view noticeably.
But I suspect you'll probably just have to figure out which views have the gestures that are interfering with yours and make them require yours to fail first.
Any chance you're colliding with one of the scrollview's gestures? It doesn't seem likely if your other gestures are working, but it might be worth at least trying the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: method in the UIGestureRecognizerDelegate protocol.