How to make restart button appear when timer ends in Swift? - swift

I'm trying to make a countdown timer in Swift.
When the timer is finished, a restart button appears and I want to make the timer restart.
This is My code so far.
class TimerViewController: UIViewController {
let duration: Double = 5 // duration in seconds
var timer: Timer?
var progressView: UIProgressView!
var timeElapsed: Double = 0
override func viewDidLoad() {
super.viewDidLoad()
// Create the progress view
progressView = UIProgressView(progressViewStyle: .default)
progressView.frame = CGRect(x: 20, y: 100, width: view.frame.width - 40, height: 10)
progressView.progress = 0
view.addSubview(progressView)
// Create the timer
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
#objc func timerFired() {
timeElapsed += 0.01
let progress = Float(timeElapsed / duration)
progressView.progress = progress
if timeElapsed >= duration {
timer?.invalidate()
timerEnded()
}
}
func timerEnded() {
// Timer has ended, do something here
// Show the restart button
print("Ended")
let restartButton = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
restartButton.setTitle("Restart", for: .normal)
restartButton.addTarget(self, action: #selector(restartTimer), for: .touchUpInside)
view.addSubview(restartButton)
}
#objc func restartTimer() {
// Restart the timer
timeElapsed = 0
progressView.progress = 0
timer?.invalidate()
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}
The problem is timer Progress bar appears well, but restart button doesn't appear.

You might've already figured it out but in-case you didn't, I believe the button is there it's just that you cannot see it. Set a background colour and you should be able to see the button
Add the below statement after setting the button title in your timerEnded button
restartButton.backgroundColor = .gray
See if this helps.

Related

Defining a #selector method when it can't access a programmatically created UILabel

If you define a UILabel programmatically, it must be done so in viewDidLoad()? But, if you were to define a #selector method that needed to set said label, it would be impossible given that the #objc selector method needs to be outside the scope of the viewDidLoad() method, where it cannot access the label. How do you get around this?
class ViewController: UIViewController {
var count = 4
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
label.center = CGPoint(x: 250, y: 430)
label.font = .systemFont(ofSize: 130)
label.textColor = UIColor.black
view.addSubview(label)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: <#T##Selector#>, userInfo: <#T##Any?#>, repeats: <#T##Bool#>)
#objc func updateLabel() { // ERROR #objc can only be used with members of classes...
if count > 0 {
count -= 1
label.text = "\(count)"
}
}
}
}
Your mistake is that you have put the selector updateLabel inside of viewDidLoad. You should put it outside viewDidLoad so that it is a member of your class, rather than a local function.
To access the label in updateLabel, simply move the declaration of the label outside of both methods.
You should also add a timer parameter to updateLabel so that you can stop the timer when count reaches 0, rather than keeping the timer running forever.
var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
label = UILabel(frame: CGRect(x: 0, y: 0, width: 150, height: 150))
label.center = CGPoint(x: 250, y: 430)
label.font = .systemFont(ofSize: 130)
label.textColor = UIColor.black
view.addSubview(label)
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateLabel), userInfo: nil, repeats: true)
}
#objc func updateLabel(_ timer: Timer) {
if count > 0 {
count -= 1
label.text = "\(count)"
} else {
timer.invalidate()
}
}

How can I escape my numpad/keyboard for my 30 textfield inputs?

I just implemented about 30 textfield inputs and after running my app I realized that I am not able to escape the numpad in anyway?? Has anyone figured out a simple solution to escape/remove the numbpad/keyboard?? I found the solution of adding a toolbar to the keyboard but that would require me to do that 30 times which doesn't sound efficient to me.
Use this
extension UITextField {
#IBInspectable var doneAccessory: Bool {
get {
return self.doneAccessory
}
set (hasDone) {
if hasDone {
addDoneButtonOnKeyboard()
}
}
}
func addDoneButtonOnKeyboard() {
let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
doneToolbar.barStyle = .default
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))
let items = [flexSpace, done]
doneToolbar.items = items
doneToolbar.sizeToFit()
self.inputAccessoryView = doneToolbar
}
#objc func doneButtonAction() {
self.resignFirstResponder()
}
}
and add this single line to your text fields
textField.doneAccessory = true

use timer to countdown from what is placed in the uitextfield

My swift code below should take whatever number is placed in the textfield and then count it down in by a single second. I dont know how to take what is in the textfield and get it to be counted down from. Dont worry about making sure the user places a number and not a string in the textfield.
import UIKit
class ViewController: UIViewController {
var enterTime = UITextField()
var lblTime = UILabel()
var startBTN = UIButton()
var timer = Timer()
var counter = 33
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[enterTime,lblTime,startBTN].forEach{
$0.backgroundColor = .systemRed
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
enterTime.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 60, height: 50)
lblTime.frame = CGRect(x: view.center.x-115, y: view.center.y, width: 60, height: 50)
startBTN.frame = CGRect(x: view.center.x-115, y: view.center.y+200, width: 60, height: 50)
startBTN.addTarget(self, action: #selector(startHit), for: .touchDown)
}
#objc func startHit() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
#objc func timerAction() {
//This is where the user would take the time from uitextfield and causes it to count down
enterTime.text = counter
lblTime.text = "\(counter)"
}
}
1 ) Invalidate your timer and initiate counter with a value from enterTime textField and start your timer in startHit function.
2 ) In the timerAction function, reduce the counter value by 1 and set it in the lblTime.
You may need to modify your startHit and timerAction functions as follows.
#objc func startHit() {
timer.invalidate()
if let counterText = enterTime.text, let counterValue = Int(counterText) {
counter = counterValue
lblTime.text = String(counter)
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
}
#objc func timerAction() {
counter -= 1
lblTime.text = String(counter)
if ( counter == 0 ) {
timer.invalidate()
}
}

Update Progress Bar every milliseconds

I want my Progress Bar to update every milliseconds. How would I do this? Currently, it updates every second, which is not what I want.
EDIT: Sorry, I forgot to mention that I still want the timer label to update every second (So it will go down by seconds not milliseconds: 10, 9, 8) while updating the progress bar every milliseconds or 25 times every second.
Code:
progressBar.transform = progressBar.transform.scaledBy(x: 1, y: 5)
progressBar.layer.cornerRadius = 5
progressBar.clipsToBounds = true
progressBar.layer.sublayers![1].cornerRadius = 5
progressBar.subviews[1].clipsToBounds = true
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)
}
#objc func timerUpdate() {
if timeRemaining <= 0 {
progressBar.setProgress(Float(0), animated: false)
bonusTimerLabel.text = "0"
bonusTimerLabel.textColor = UIColor(red: 186/255, green: 16/255, blue: 16/255, alpha: 1)
} else {
progressBar.setProgress(Float(timeRemaining)/Float(10), animated: false)
timeRemaining -= 1
bonusTimerLabel.text = "\(timeRemaining)"
}
Firing a function with timer every millisecond is not advisable, reference: https://stackoverflow.com/a/30983444/8447312
So you can fire timer func every 50 millisecond to be safe and update your progress bar. That shouldn't be too observable though.
Also make sure timeRemaining is a Double, and then just try:
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 0.050, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)
}
#objc func timerUpdate() {
if timeRemaining <= 0 {
progressBar.setProgress(Float(0), animated: false)
bonusTimerLabel.text = "0"
bonusTimerLabel.textColor = UIColor(red: 186/255, green: 16/255, blue: 16/255, alpha: 1)
} else {
progressBar.setProgress(Float(timeRemaining)/Float(20), animated: false)
timeRemaining -= 0.050
bonusTimerLabel.text = "\(Int(timeRemaining))"
}
1 millisecond is 0.001 second.
So change timerInterval property in timer to 0.001:
timer = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(timerUpdate), userInfo: nil, repeats: true)

swift UIview removeFormSuperview

this is a test code,I want to add uiview to storyBoard ,and wait 1 second, and remove it .but the uiview doesn't appear , the code is down
var uiview1 = UIView()
uiview1.frame = CGRectMake(0, 0, 50, 50)
uiview1.backgroundColor = UIColor.blackColor()
self.view.addSubview(uiview1)
sleep(1)
uiview1.removeFromSuperview()
sleep() is not a good idea in this way. It will become totally unresponsive and block. Use NSTimer.scheduledTimerWithTimeInterval instead.
class ViewController: UIViewController {
var uiview1 = UIView()
override func viewDidLoad() {
super.viewDidLoad()
uiview1.frame = CGRectMake(0, 0, 50, 50)
uiview1.backgroundColor = UIColor.blackColor()
self.view.addSubview(uiview1)
var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("dismissView"), userInfo: nil, repeats: false)
}
func dismissView() {
uiview1.removeFromSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}