Why is there a delay when moving object using UIPanGestureRecognizer?
Asked Answered
M

3

17

I'm moving UIView object using UIPanGestureRecognizer — how much I drag my finger on screen, that much I move the view in the same direction (only in X - left or right, Y is not changing). It works fine, but with (very noticeable) delay.

Here is the method that handles the UIPanGestureRecognizer event:

-(void)movePages:(UIPanGestureRecognizer *)sender
{
    if (switchingMode == 1) {
        if ([sender state] == UIGestureRecognizerStateBegan) {
            fingerStartPosition = [sender locationInView:self.view].x;
            viewStartPosition = [[viewControllers objectAtIndex:activeViewControllerIndex] view].center;
        }
        [[[[viewControllers objectAtIndex:activeViewControllerIndex] view] layer] setPosition:CGPointMake(viewStartPosition.x - (fingerStartPosition - [sender locationInView:self.view].x) , viewStartPosition.y)];            
    }
}

I've tried to set position of the view using its layer, I've also tried setting the frame, using animations with different durations, but everything behaved the same. Any idea why this delay occurs ?

Malefic answered 23/5, 2012 at 21:24 Comment(1)
Did you find a solution? Also having itWanderlust
F
47

Use a UILongPressGestureRecognizer and set the minimumPressDuration to 0.0. This recognizes instantly and you get all the same updates including the UIGestureRecognizerStateChanged with the updated location.

Fergus answered 1/12, 2012 at 19:38 Comment(6)
Thought I would add - I was having an issue with a drawing field on a form when the form was zoomed (scaled) in - if you start drawing straight away it would scroll instead, you would have to hold your finger in place for a split second before drawing. Doing this at 100% scale it didn't happen, it would start drawing instantly. Replacing the UIPanGestureRecognizer with UILongPressGestureRecognizer worked a treat!Supination
You'll also want to set the UILongPressGestureRecognizer's allowableMovement to CGFloat.infinity. Otherwise, the gesture will cancel itself after dragging a short distance (10pt).Byars
Strangely, to fix the exactly same issue than @EvilGeniusJamie, for me, it was the allowable movement mentionned by Slipp that I had to set. It was requiring a longer and longer distance once zoomed to 1.5. I had to drag at the speed of light to prevent the UIPan from cancelling and be able to trigger the Move event. Setting it to infinity did work.Barbary
But with this solution, you won't have translation(in:) method, which is the reason why we need a UIPanGestureRecognizer :/Disadvantageous
It works, but there is no velocity when using long press recognizer.Brittnee
unfortunately, sadly, this is wrong in 2020 with recent iOS. it will detect the start of the gesture instantly BUT the first .moved report won't happen for some 8 or so pixels. (even if you try the allowableMovement trick.) Apple's gestures are useless for detailed, tiny movements. Just use touchesBegan ...Bijugate
S
5

I found that it was faster responding if you use just regular touchesBegan, Moved and Ended. I even subclassed a UIGestureRecognizer, and it still had lag on the panning gesture. Even though the touchesBegan within the UIGestureRecognizer would trigger on time, the state change would take a half second to change its state... It seems faster to just use a plain old TouchesBegan, especially if you're cpu is doing a lot.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    if touches.count == 1
    {
        initialTouchLocation = (touches.first?.locationInView(self).x)!
    }
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
{
    if touches.count == 1
    {
        let locationInView = touches.first?.locationInView(self)
        if !thresholdHit
        {
            //this is the threshold for x movement in order to trigger the panning...
            if abs(initialTouchLocation - locationInView!.x) > 1
            {
                thresholdHit = true
            }
        }
        else
        {
            if (self.frame.width != CGFloat(screenSize))
            {
                let panDelta = initialTouchLocation - locationInView!.x
            }
        }
    }
}
Scorpio answered 19/2, 2016 at 6:34 Comment(2)
Agreed. In my case, I want to highlight the item I'm dragging around. For this to look good, the highlight needs to be drawn immediately. Seems as though the UIGestureRecognizer stuff is a tad slow at sending that initial action to the target which is just no use at all for my use case.Allyl
note - just use the superview (location in ..) to prevent jitter, if you are actually moving the viewBijugate
K
3

The GestureRecognizer can't be sure, if it is a pan gesture, before you moved your finger some pixels. I don't know the exact tolerance value, but that is why you feel a delay.

Documentation:

A panning gesture is continuous. It begins when the minimum number of fingers allowed have moved enough to be considered a pan.

If you want instant movement, you probably need to build your own logic using touchesMoved:.

Another approach could be, to animate to the first recognized point. But that doesn't remove the delay. For that approach you could have a look at my JDDroppableView on github.

Kagu answered 23/5, 2012 at 21:29 Comment(1)
it is not the delay at the beginning of the dragging I mean... when this delay is over, and object is already moving, it's moving with delay. I drag, object starts moving, but it's kind of slow and I have to wait until the object gets to the required positionMalefic

© 2022 - 2024 — McMap. All rights reserved.