18

I'm working on a game in which an attribute of a game object is set by long pressing on the object itself. The value of the attribute is determined by the duration of the long press gesture. I'm using UILongPressGestureRecognizer for this purpose, so it's something like this:

[gameObjectView addGestureRecognizer:[[UILongPressGestureRecognizer alloc] 
                                       initWithTarget:self action:@selector(handle:)]];

Then the handler function

- (void)handle:(UILongPressGestureRecognizer)gesture {
  if (gesture.state == UIGestureRecognizerStateEnded) {
    // Get the duration of the gesture and calculate the value for the attribute
  }
}

How do I get the duration of the long press gesture in this case?

Ryan Wersal
  • 3,210
  • 1
  • 20
  • 29
Ryan Dao
  • 321
  • 1
  • 4
  • 13

7 Answers7

29

I'm pretty sure the gesture doesn't store this information for you to access. You can only set a property on it called minimumPressDuration that is the amount of time before the gesture is recognised.

Workaround with ios 5 (untested):

Create an NSTimer property called timer: @property (nonatomic, strong) NSTimer *timer;

And a counter: @property (nonatomic, strong) int counter;

Then @synthesize

- (void)incrementCounter {
    self.counter++;
}

- (void)handle:(UILongPressGestureRecognizer)gesture {
    if (gesture.state == UIGestureRecognizerStateBegan) {
         self.counter = 0;
         self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(incrementCounter) userInfo:nil repeats:yes];
    }
    if (gesture.state == UIGestureRecognizerStateEnded) {
        [self.timer invalidate];
    }
}

So when the gesture begins start a timer that fires the incrementation method every second until the gesture ends. In this case you'll want to set the minimumPressDuration to 0 otherwise the gesture won't start straight away. Then do whatever you want with counter!

Rupert Horlick
  • 671
  • 5
  • 15
  • Check the code above. Hopefully it'll work for you! If you're not using ios 5 you may need to make some changes. – Rupert Horlick Feb 23 '12 at 21:33
  • 1
    I was avoiding creating a property just for this purpose, but I guess there's no other way. I use NSDate instead of NSTimer so the code can be cleaner. Thanks for your answer! – Ryan Dao Feb 24 '12 at 05:32
9

No timers needed. You can achieve it this way:

- (void)handleRecognizer:(UILongPressGestureRecognizer *)gesture
{
    static NSTimeInterval pressStartTime = 0.0; //This can be moved out and kept as a property
    
    switch ([gesture state])
    {
        case UIGestureRecognizerStateBegan:
            //Keeping start time...
            pressStartTime = [NSDate timeIntervalSinceReferenceDate];
            break; /* edit*/
        case UIGestureRecognizerStateEnded:
        {
            //Calculating duration
            NSTimeInterval duration = [NSDate timeIntervalSinceReferenceDate] - pressStartTime;
            //Note that NSTimeInterval is a double value...
            NSLog(@"Duration : %f",duration);
            break;
        }
        default:
            break;
    }
}

Also, don't forget to set the gesture recognizer's minimumPressDuration to 0 while creating it, if you want to get the real duration of the long press:
myLongPressGestureRecognizer.minimumPressDuration = 0

Just Shadow
  • 10,860
  • 6
  • 57
  • 75
6

It seems by far the cleanest and simplest solution in object oriented Cocoa Touch is to subclass UILongPressGesture. Here is an example written in Swift.

    class MyLongPressGesture : UILongPressGestureRecognizer {
        var startTime : NSDate?
    }

    func installGestureHandler() {
            let longPress = MyLongPressGesture(target: self, action: "longPress:")
            button.addGestureRecognizer(longPress)
    }

    @IBAction func longPress(gesture: MyLongPressGesture) {
            if gesture.state == .Began {
                    gesture.startTime = NSDate()
            }
            else if gesture.state == .Ended {
                    let duration = NSDate().timeIntervalSinceDate(gesture.startTime!)
                    println("duration was \(duration) seconds")
            }
    }

If you want to include the time from first tap, you can include it when you calculate duration, by adding back gesture.minimumPressDuration. The drawback is that it is probably not be micro-second precise, given there is likely a small (tiny) amount of time elapsing between the gesture being triggered and your .Start handler being called. But for the vast majority of applications that shouldn't matter.

SafeFastExpressive
  • 3,637
  • 2
  • 32
  • 39
4

See the "minimumPressDuration" property. According to the documentation:

The minimum period fingers must press on the view for the gesture to be recognized.

[...]

The time interval is in seconds. The default duration is is 0.5 seconds.

fbernardo
  • 10,016
  • 3
  • 33
  • 46
  • The question is about duration, i.e. how long the user held their finger down, minimumPressDuration is for setting the delay before the gesture is triggered. – SafeFastExpressive Feb 17 '15 at 21:31
  • For those whole wish to not hardcode a 'magic number', use this to get the default: `[UILongPressGestureRecognizer new].minimumPressDuration` – Albert Renshaw Mar 05 '21 at 08:55
1

I know this is a late answer but this works perfectly for me in IOS 7 & 8 without having to create a timer.

UILongPressGestureRecognizer *longGR = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(yourAction)]; // create the recognizer
longGR.minimumPressDuration = 10.0f; //Ten Seconds
longGR.allowableMovement = 50.0f; //Allowable Movement while being pressed
[gameObjectView setUserInteractionEnabled:YES]; //If setting interaction to a non-interactable object such as a UIImageView
[gameObjectView addGestureRecognizer:longGR]; //Add the gesture recognizer to your object
inVINCEable
  • 2,167
  • 3
  • 25
  • 49
1

You can get it by Following in Swift 3.0

Logic : Just assigning the time of press and find the difference of time when touch ends

Code :

//variable to calculate the press time
static var pressStartTime: TimeInterval = 0.0

func handleRecognizer(gesture: UILongPressGestureRecognizer) -> Double {
    var duration: TimeInterval = 0

    switch (gesture.state) {
    case .began:
        //Keeping start time...
        Browser.pressStartTime = NSDate.timeIntervalSinceReferenceDate

    case .ended:
        //Calculating duration
        duration = NSDate.timeIntervalSinceReferenceDate - Browser.pressStartTime
        //Note that NSTimeInterval is a double value...
        print("Duration : \(duration)")

    default:
        break;
    }

    return duration
}
Janmenjaya
  • 4,149
  • 1
  • 23
  • 43
0

It seems that with new iOS versions (10 for me at the moment), the long press recognizer callbacks for both the .begin and .ended states happen one after the other, only after the event has ended, that is only when you raise your finger from the screen.

That makes the difference between the two events be fractions of milliseconds, and not what you were actually searching for.

Until this will be fixed, I decided to create another gesture recognizer from scratch in swift, and that would be its working skeleton.

import UIKit.UIGestureRecognizerSubclass
class LongPressDurationGestureRecognizer : UIGestureRecognizer {
        private var startTime : Date?
        private var _duration = 0.0
        public var duration : Double {
            get {
                return _duration
            }
        }
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
            startTime = Date() // now
            state = .begin
            //state = .possible, if you would like the recongnizer not to fire any callback until it has ended
        }
        override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
            _duration = Date().timeIntervalSince(self.startTime!)
            print("duration was \(duration) seconds")
            state = .ended
            //state = .recognized, if you would like the recongnizer not to fire any callback until it has ended
        }
 }

Then you can access the .duration property of the LongPressDurationGestureRecognizer gesture recognizer easily.

For instance:

func handleLongPressDuration(_ sender: LongPressDurationGestureRecognizer) {
        print(sender.duration)
}

This specific example, doesn't take into consideration how many touches actually took place, or their location. But you can easily play with it, extending the LongPressDurationGestureRecognizer.

Liron Berger
  • 456
  • 5
  • 3