0

I am trying to show a button only when user starts panning for 0.3 seconds, to avoid showing the button instantaneously in some cases (e.g. at the end of zooming if fingers are not lifted together). To achieve that, I start a Timer when pan gesture is in .began state, then showing the button in .changed state only when timer becomes nil.

However, my Timer never runs until I lift my finger (i.e. gesture is .ended). I figured it probably has to do with run loop and gesture occupying the main thread? Any workaround would be appreciated. Thanks!

var timer: Timer?

func handler(_ sender: UIPanGestureRecognizer) {
    switch sender.state {
    case .began:
        if timer == nil {
            // I just want timer to invalidate itself after firing, so nothing to execute
            self.timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false, block: { _ in })
    case .changed:
        if button.isHidden && timer == nil {
            button.isHidden = false
        }
    case .ended:
        button.isHidden = true
        timer?.invalidate()
        timer = nil
    default:
        return
    }
}
Jack Guo
  • 3,959
  • 8
  • 39
  • 60
  • 2
    There's no code in your execution block; have you omitted this deliberately? – Ash Feb 09 '18 at 14:07
  • No, I just want the timer to invalidate itself after firing, so I don't want it to execute anything. – Jack Guo Feb 09 '18 at 15:19
  • 1
    Well, invalidating a timer only removes it from the run loop. The creating class is still holding a reference to it, so it's not going to become nil on its own. If you're dead set on doing it this way, add `weak` in front of `var timer: Timer?` – Ash Feb 09 '18 at 15:25
  • Got it, but your solution still doesn't solve the problem that timer never runs before pan gesture ended – Jack Guo Feb 09 '18 at 16:38

2 Answers2

0

If your main goal is to just show the button AFTER the user starts panning for 0.3 seconds, all you need to do is detect when the pan gesture is moved, and then show the button. You don't even need create the timer on gesture begin.

var timer: Timer?

func handler(_ sender: UIPanGestureRecognizer) {

    switch sender.state {
    case .changed:
        if (timer == nil || timer?.isValid == false) {
            timer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { (_) in
                // Show button
                self.button.isHidden = false
            }
        }
    case .ended :
        button.isHidden = true
        timer?.invalidate()
    default:
        return
    }
}
Chan Jing Hong
  • 2,251
  • 4
  • 22
  • 41
-1

Try this:

    var timer: Timer?

    func handler(_ sender: UIPanGestureRecognizer) {
        switch sender.state {
        case .began:
            if timer == nil {
                self.timer = Timer.scheduledTimer(
                withTimeInterval: 0.3, 
                repeats: false, 
                block: { [weak self] _ in
                   self?.button.isHidden = false 
                })
        case canceled, .ended:
            timer?.invalidate()
            timer = nil
        default:
            return
        }
    }
Ash
  • 9,064
  • 3
  • 48
  • 59