0

I'm trying to create an app that is similar to SnapChat when it takes videos. You are supposed to hold down a button to start taking a video and then release it when you are done. I'm trying to implement a timeout feature after 7 seconds and also a progress bar by using a Timer object. However, when I try fire the timer, it only calls the given selector once and that's it even though I tell it to repeat itself.

Here's my code to initialize the button:

let photoButton:UIButton = {
    let but = UIButton(type: .custom)
    but.layer.cornerRadius = 40
    but.layer.borderColor = UIColor.white.cgColor
    but.layer.borderWidth = 4
    but.clipsToBounds = true
    but.addTarget(self, action: #selector(takeVideo), for: .touchDown)
    but.addTarget(self, action: #selector(stopVideo), for: [.touchUpInside, .touchUpOutside])
    but.translatesAutoresizingMaskIntoConstraints = false
    return but
}()

Here's the function takeVideo that is called when the user starts to hold down the button. I initialize and fire the Timer object here:

@objc func takeVideo() {
    progressBar.isHidden = false

    timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgressbar), userInfo: nil, repeats: true)
    startTime = Date().timeIntervalSince1970()
    timer?.fire()
}

This is the function stopVideo where I invalidate the Timer object:

@objc func stopVideo() {
    timer?.invalidate()
    // stop video
}

And this is the updateProgressBar function:

@objc private func updateProgressbar() {
    let maxLength = 7.0
    let difference = Date().timeIntervalSince1970 - startTime!

    progressBar.progress = Float(difference / maxLength)
    if difference >= maxLength {
        stopVideo() // Invalidates the timer and will stop video. 
    }
}

It seems that this person has had a similar issue, but when I tried to incorporate his answer of presenting the current view controller using async, it still doesn't work. Here's how I present the video recording view controller:

let recorder = RecorderViewController()
recorder.db = db
DispatchQueue.main.async(execute: {
    self.present(recorder, animated: true, completion: nil)
})

Edit: I've fixed the incorrectness concerning the Timer object's fireDate property (the fix is also fixed in the above code). It fixed my problem.

kbunarjo
  • 1,277
  • 2
  • 11
  • 27
  • 1
    A timer on which you have called `invalidate` can never be used again. If you need to perform the same thing again, make a new timer. – matt May 22 '18 at 16:14
  • it doesn't get invalidated until after the user releases the button though. while the user is holding the button down, the timer should continue to call the update function – kbunarjo May 22 '18 at 17:09
  • 2
    I think you are misunderstanding what the fireDate property of a timer is. This is the date in the future that the timer is next scheduled to fire. It's not the date at which it started firing or last fired. So you are comparing the date now to a date in the future and expecting it to eventually get greater than 7. You need to record the date the timer first starts firing and then use that to compare to determine when to timeout. – Upholder Of Truth May 22 '18 at 20:59

0 Answers0