i am making an application where i am using OTP screen . i worked out with the timer functionality and also able to change the text and show the timer again . my issue is i am not able to restart the timer again. it gets stuck in 00:10.
Here is my following code please Help !!
#IBOutlet weak var butttonName: ButtonDesign!
var countTimer:Timer!
var counter = 10
var restartTimer = false
var isTimerRunning = false
override func viewDidLoad() {
if isTimerRunning == false {
startTimer()}
}
func startTimer() {
self.countTimer = Timer.scheduledTimer(timeInterval: 1 , target: self, selector: #selector(OTPVerificationViewController.changeTitle), userInfo: nil, repeats: true)
isTimerRunning = true
}
#objc func changeTitle()
{
if counter != 0
{
butttonName.setTitle(timeString(time: TimeInterval(counter)), for: .normal)
counter -= 1
butttonName.isEnabled = false
} else {
countTimer.invalidate()
butttonName.setTitle("Resend", for: .normal)
}
}
#IBAction func verifyButton(_ sender: UIButton) {
if butttonName.currentTitle == "Resend" {
print("clicked when name is resend !!!")
if restartTimer == true {
startTimer()
restartTimer = false
}
} else {
print("clicked when it is name is verify")
self.performSegue(withIdentifier: "confirmPassword", sender: self)
}
}
I want to restart the timer whenever the user clicked on "Resend" Button and want to perform segue whenever the user clicked on "Verify" button . both button are the same one i am changing the name on run time
You need to restart the counter too
if butttonName.currentTitle == "Resend" {
counter = 10
// startTimer()
}
Just make the method just like below:
var count = 120
func updateCounter(){
if #available(iOS 10.0, *) {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { (timer) in
if(self.count > 0){
self.lblTime.text = "\(self.count)"
self.count = self.count-1
}else {
self.lblTime.text = "0"
timer.invalidate()
self.btnresend.isHidden = false // in your case you can change the title of the button
}
})
} else {
// Fallback on earlier versions
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(self.timerMethod), userInfo: nil, repeats: true)
}
}
#objc func timerMethod() {
if(self.count > 0){
self.lblTime.text = "\(self.count)"
self.count = self.count-1
}else {
self.lblTime.text = "0:0"
self.btnresend.isHidden = false
}
}
And from where you want to restart the time just call the update counter method
with set count 120 or you want
self.count = 120
Related
I want to set a timer of 30 seconds to send an OTP to a phone number. And while the timer is running, the resend OTP button should be disabled. Once the timer ends, the resend OTP button should get enabled and the timer label should be hidden. Onclick of the resend OTP button, the same process should continue.
In the code that I have written, the timer label is hidden the entire time and is constantly going into the if loop where it is constantly printing "invalidated".
Below is the code that I have written.
Updated Code:
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var resendOTPBtn: UIButton!
var countdownTimer: Timer!
var totalTime = 30
override func viewDidLoad() {
super.viewDidLoad()
if AFWrapper.isConnectedToInternet() {
timerLabel.text = ""
sendOTPCode()
sendOTPAPICall()
resendOTPBtn.isEnabled = false
} else {
self.textPopupAlert(title: ALERT_TITLE, message: OFFLINE_MSG)
}
}
// in case user closed the controller
deinit {
countdownTimer.invalidate()
}
#objc func updateTimerLabel() {
totalTime -= 1
timerLabel.text = "\(timeFormatted(totalTime))"
if totalTime == 0 {
timerLabel.text = ""
countdownTimer.invalidate()
resendOTPBtn.isEnabled = true
}
}
#IBAction func resendOTPBtnClicked(_ sender: Any) {
if AFWrapper.isConnectedToInternet() {
sendOTPAPICall()
timerLabel.text = ""
totalTime = 31
resendOTPBtn.isEnabled = false
sendOTPCode()
}
else {
self.textPopupAlert(title: ALERT_TITLE, message: OFFLINE_MSG)
}
}
func sendOTPCode() {
self.countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimerLabel), userInfo: nil, repeats: true)
}
Initial Code:
#IBAction func resendOTPBtnClicked(_ sender: Any) {
if AFWrapper.isConnectedToInternet() {
sendOTPAPICall()
countDownTime()
}
else {
self.textPopupAlert(title: ALERT_TITLE, message: OFFLINE_MSG)
}
}
func countDownTime() {
DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
self.timerLabel.isHidden = false
self.resendOTPBtn.isEnabled = false
if self.timerLabel.isHidden == false {
self.startTimer()
} else {
self.countdownTimer.invalidate()
self.resendOTPBtn.isEnabled = true
self.timerLabel.isHidden = true
}
}
}
// Method to start the timer when resend OTP button is clicked.
func startTimer() {
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#objc func updateTime() {
DispatchQueue.main.async(){
self.timerLabel.text = self.timeFormatted(self.totalTime)
if self.totalTime != 0 {
self.totalTime -= 1
} else {
print("Invalidated")
self.endTimer()
}
}
}
func timeFormatted(_ totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
let minutes: Int = (totalSeconds / 60) % 60
// let hours: Int = totalSeconds / 3600
return String(format: "%02d:%02d", minutes, seconds)
}
Any solutions would be appreciated. Thank you!
#IBOutlet weak var resendCodeTimerLabel: UILabel!
#IBOutlet weak var resendCodeButton: UIButton!
var resendCodeCounter = 30
var resendCodeTimer = Timer()
override func viewDidLoad() {
super.viewDidLoad()
resendCodeTimerLabel.text = ""
sendOTPCode()
}
// in case user closed the controller
deinit {
resendCodeTimer.invalidate()
}
#objc func updateTimerLabel() {
resendCodeCounter -= 1
resendCodeTimerLabel.text = "Resend code in \(resendCodeCounter) seconds."
if resendCodeCounter == 0 {
resendCodeButton.isEnabled = true
resendCodeTimer.invalidate()
}
}
#IBAction func resendAgainButtonClicked(_ sender: UIButton) {
OTPTextField.text = ""
resendCodeCounter = 31
resendCodeButton.isEnabled = false
sendOTPCode()
}
func sendOTPCode() {
//Whatever your api logic
if otpSent {
self.resendCodeTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateTimerLabel), userInfo: nil, repeats: true)
}
}
I want to make my label change text every 5 seconds from rLabel.text = "\(update) free trial" wait for another 5 second then change it rLabel.text = "upgrade now!" ~ on repeat.
rLabel.text = "\(update) free trial"
if daysLeft <= 5 {
let timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
timer.fire()
}
} else {
rLabel.text = "\(update) free trial"
}
#objc func update() {
rLabel.text = "upgrade now!"
}
You can simply use a boolean to switch the text every 5 sec
override func viewDidLoad() {
let timer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
}
var isShowingText1 = false
#objc func updateText(){
rLabel.text = isShowingText1 ? "Text 2" : "Text 1"
isShowingText1 = !isShowingText1
}
You can keep track of current text by having index property. Also you can use Timer with block closure and you can avoid having Obj-C-ish #selector
var index = 0 {
didSet {
if index > 1 { index = 0 }
}
}
var text: String {
return ["\(update) free trial", "upgrade now!"][index]
}
func foo() {
rLabel.text = text
Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
self.index += 1
self.rLabel.text = self.text
}
}
I'm building an app in Swift 3. When I press start the first time my timer begins, but when I pause it and try to press start again, the timer does not budge. To give context, the timer, with an amount of time attached to it, is selected from a table. each time the timer load, the start button works initially.
protocol TimerViewControllerDelegate: class {
func viewController(_ controller: ViewController, didFinishEditing item: TaskData)
}
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var pauseButton: UIButton!
#IBOutlet weak var startButton: UIButton!
#IBOutlet weak var timerTaskName: UILabel!
#IBOutlet weak var timerTimeSetting: UILabel!
#IBOutlet weak var progressView: UIProgressView!
weak var delegate: TimerViewControllerDelegate?
var timerTask: TaskData?
var timer: Timer?
var progressViewSpeed: Double = 0.0
#IBAction func cancel(_ sender: Any) {
timer?.invalidate()
dismiss(animated: true, completion: nil)
delegate?.viewController(self, didFinishEditing: timerTask!)
}
#IBAction func startButtonTapped(_ sender: Any) {
timerTask?.startTime = Date()
runTimer()
if timerTask?.isTaskRunning == true {
runTimer()
self.startButton.isEnabled = false
self.pauseButton.isEnabled = true
} else {
//retrieve start time and run
timerTask?.startTime = Date()
runTimer()
self.startButton.isEnabled = false
self.pauseButton.isEnabled = true
}
}
func runTimer() {
guard timer == nil else {
return
}
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
}
#IBAction func pauseButtonTapped(_ sender: UIButton) {
if timerTask?.isTaskRunning == true {
timer?.invalidate()
if let timerTask = timerTask, timerTask.isTaskRunning {
// Calculate the difference between now and when the timerTask was started
let difference = Int(Date().timeIntervalSince(timerTask.startTime!))
timerTask.taskRemaining -= difference
if timerTask.taskRemaining == 0 {
// Do something when there's no time remaining on the task?
}
timerTask.startTime = nil
}
}
else {
timerTask?.startTime = Date()
runTimer()
self.pauseButton.setTitle("Pause",for: .normal)
}
self.startButton.isEnabled = true
self.pauseButton.isEnabled = false
}
/*
#IBAction func resetButtonTapped(_ sender: Any) {
timer.invalidate()
seconds = 60
self.timerLabel.text = timeString(time: TimeInterval(seconds))
if self.resumeTapped == true {
self.resumeTapped = false
self.pauseButton.setTitle("Pause",for: .normal)
}
isTimerRunning = false
pauseButton.isEnabled = false
startButton.isEnabled = true
}
*/
func updateTimer() {
guard let timerTask = timerTask else {
return
}
if timerTask.taskRemaining < 1 {
timer?.invalidate()
timer = nil
//Send alert to indicate "time's up!"
} else {
updateTime()
}
progressViewSpeed = 1 / Double(timerTask.taskRemaining)
progressView.progress += Float(progressViewSpeed)
}
func timeString(time:TimeInterval) -> String {
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
override func viewDidLoad() {
super.viewDidLoad()
guard let timerTask = timerTask else {
return
}
if timerTask.isTaskRunning {
startButton.isEnabled = false
pauseButton.isEnabled = true
runTimer()
} else {
startButton.isEnabled = true
pauseButton.isEnabled = false
}
timerTaskName.text = timerTask.task
updateTime()
self.progressView.transform = CGAffineTransform.identity.rotated(by: CGFloat.pi / 2).scaledBy(x: 1, y: 150)
}
func updateTime() {
guard let timerTask = timerTask else {
return
}
if let startTime = timerTask.startTime {
// Calculate the difference between now and when the timerTask was started
let difference = Int(Date().timeIntervalSince(startTime))
if timerTask.taskRemaining == difference {
// Do something when there's no time remaining on the task
timer?.invalidate()
timer = nil
}
timerLabel.text = timeString(time: TimeInterval(timerTask.taskRemaining - difference))
} else {
timerLabel.text = timeString(time: TimeInterval(timerTask.taskRemaining))
}
}
}
Once you've invalidated an NSTimer, you can't use it again. You should create the new object.
See here for more From NSTimer Docs
Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes and releases the timer, either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.
You need to invalidate it and recreate it. "isPaused" bool to keep track of the state
var isPaused = true
var timer: Timer?
#IBAction func pauseResume(sender: AnyObject) {
if isPaused{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(ViewController.updateTimer)), userInfo: nil, repeats: true)
pauseButton.isHidden = false
startButton.isHidden = true
isPaused = false
} else {
pauseButton.isHidden = true
startButton.isHidden = false
timer.invalidate()
isPaused = true
}
}
I am attempting to make a countdown timer that counts down from 60 seconds and then stops when it gets to 0. But for the timer keeps going into negative seconds. Any advice is appreciated. Code:
#IBOutlet var timeCounter: UILabel!
var second = 60
var timer = NSTimer()
var timerRunning = true
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setTimer()
timerCounting()
}
func setTimer(){
second -= 1
timeCounter.text = "\(second)"
}
func timerCounting(){
if(timerRunning == true){
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("setTimer"), userInfo: nil, repeats: true)
timerRunning = true
if second == 0 {
timerRunning = false
timer.invalidate()
}
}
}
You have to move the invalidation into the setTimer function since at its current location will never be triggered because timerCounting is only called once - in the beginning.
func setTimer(){
second -= 1
timeCounter.text = "\(second)"
if second == 0 {
timerRunning = false
timer.invalidate()
}
}
play with this in playground
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
import Foundation
#objc class C:NSObject {
var timer: NSTimer?
var second = 5
func setTimer(){
if c.second < 0 {
print("the timer fires the last time here ...")
timer?.invalidate()
} else {
print(second)
second -= 1
}
}
}
let c = C()
func timerCounting(){
c.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: c, selector: Selector("setTimer"), userInfo: nil, repeats: true)
}
timerCounting()
I made a stopwatch in swift. It has three buttons- play, stop, reset. it has a display in the middle where I display the seconds passed.
My code runs fine. But the watch runs very fast after the stop or reset.
I am not able to figure out what is the fuss.
please help!
var time = 0
var timer = NSTimer()
var pause = Bool(false)
#IBOutlet var result: UILabel!
func displayResult() {
if pause != true
{
time++
result.text = String(time)
}
}
#IBAction func reset(sender: AnyObject) {
time = 0
result.text = String(time)
}
#IBAction func pause(sender: AnyObject) {
pause = true
}
#IBAction func play(sender: AnyObject) {
pause = false
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("displayResult") , userInfo: nil, repeats: true)
}
Your pause action should be:
#IBAction func pause(sender: AnyObject) {
timer.invalidate()
pause = true
}
UPD:
var time = 0
var timer = NSTimer()
var pause = true // 1
func displayResult() {
if pause != true
{
time++
label.text = String(time)
}
}
#IBAction func reset(sender: AnyObject) {
time = 0
label.text = String(time)
timer.invalidate() // 2
pause = true // 3
}
#IBAction func pause(sender: AnyObject) {
timer.invalidate() // 4
pause = true
}
#IBAction func play(sender: AnyObject) {
// 5
if pause == true {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("displayResult") , userInfo: nil, repeats: true)
pause = false
}
}