How do I solve this error "Argument of '#selector' cannot refer to local function 'updateTimer()'", trying to make countdown timer in swift 5.1 - ios

import UIKit
class ViewController : UIViewController {
let eggTimes = ["Soft": 300, "Medium": 420, "Hard": 720]
var secondsRemaining = 60
#IBAction func hardnessSelected(_ sender: UIButton) {
let hardness = sender.currentTitle!
secondsRemaining = eggTimes[hardness]!
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds.")
secondsRemaining -= 1
}
}
}
}
I am taking this Udemy course where it seems to be working fine but on my system, it is not working at all.

The action method must be on the top level of the class (the eggTimes level) and must be marked as #objc.
A more convenient solution is the (underestimated) closure-based API of Timer
Replace
Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
func updateTimer() {
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds.")
secondsRemaining -= 1
}
}
with
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [unowned self] timer in
if self.secondsRemaining > 0 {
print("\(self.secondsRemaining) seconds.")
self.secondsRemaining -= 1
} else {
timer.invalidate()
}
}

Related

Unexpected Output: I am using the below code to print out the timer according to the button pressed but timer is starting with 20 only. what's wrong?

import UIKit
class ViewController: UIViewController {
let eggTimes = ["Soft": 60,"Medium": 72,"Hard": 95]
var secondsRemaining = 20
var timer = Timer()
#IBAction func hardnessSelected(_ sender: UIButton) {
let hardness = sender.currentTitle!
var secondsRemaining = eggTimes[hardness]!
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
}
#objc func timerAction()
{
if secondsRemaining > 0 {
print("\(secondsRemaining) seconds")
secondsRemaining -= 1
}
}
}
Unexpected Output: I am using the below code to print out the timer according to the button pressed but timer is starting with 20 only. what's wrong?
You are creating a new variable inside your Button function:
var secondsRemaining = eggTimes[hardness]!
instead you should assing your value. It should be:
self.secondsRemaining = eggTimes[hardness]!

Can't get timer to count down

I'm trying to learn how to use a timer in Swift, and every solution I look up is broken somehow or beyond my understanding.
I've tried with a closure, and without.
With a closure, I can actually get the app to run without crashing, but the timer just repeats 60, it doesn't count down and I don't know why.
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
var secondsRemaining = 60
print(secondsRemaining)
secondsRemaining -= 1
})
I've also tried using an #objc func with selector, but my app crashes right away with error Thread 1: "-[__SwiftValue countdown]: unrecognized selector sent to instance 0x600002970e40" (I haven't even gotten to trying the count down yet).
class ViewController: UIViewController {
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
#objc func countdown() {
print("fire")
}
…
…
}
Any help is appreciated.
EDIT
If I place my variable outside the block, I get an error inside the block Instance member 'secondsRemaining' cannot be used on type 'ViewController'
class ViewController: UIViewController {
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
But if I create a new project and put the timer inside viewDidLoad(), it works. I don't know why.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
}
Every time your timer fires, a new variable with an initial value of 60 is instantiated, thus always printing 60.
You have to declare your counting variable outside:
private var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(self.secondsRemaining)
self.secondsRemaining -= 1
})
Or:
class ViewController: UIViewController {
private var secondsRemaining = 60
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
#objc func countdown() {
print("fire")
self.secondsRemaining -= 1
}
}
You're declaring counter inside the timer call. That's why every time the timer executes it resets the counter to 60 seconds. You need to declare your timer outside the it's call:
var secondsRemaining = 60
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
As mentioned in the comments above, don't reset the variable every single time you get in the closure and change you timer to the following. Create a simple obj function to do whatever logic you want. Also name your functionalists meaningfully
class XViewController: UIViewController {
private var secondsRemaining:Int = 60
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(startScrolling), userInfo: nil, repeats: true)
#objc func startScrolling() {
print(secondsRemaining)
secondsRemaining -= 1
}
}
Here is the fixed code:
class ViewController: UIViewController {
private var secondsRemaining = 60
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { timer in
print(secondsRemaining)
secondsRemaining -= 1
})
}
}

How to add countdowntimer which will run in whole iOS application?

I need to add a global timer of 60 minutes in the application which will show in all ViewControllers.
But not able to implement this feature.
I have implemented timer in one ViewController till now.
Here is my code:
var timerCount = 3600
var timer = Timer()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timeLbl.text = minutes + ":" + seconds
timerCount -= 1
}
else {
timer.invalidate()
let vc = storyboard?.instantiateViewController(withIdentifier: "NavigateViewController")
navigationController?.pushViewController(vc!, animated: true)
}
}
I need to show this timer value in every ViewController of the application.
// Create class TimerManager
class TimerManager {
var timerCount = 3600
static let sharedInstance = TimerManager()
var timer: Timer?
init() {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timerCount -= 1
}
else {
self.timer?.invalidate()
}
}
}
// At view Controller you can access timerCount
print(TimerManager.sharedInstance.timerCount)
You should display it in a separate UIWindow instance. Put the label in a separate View Controller and refer to answer 42 from this post:
To create a new UIWindow over the main window
// Create class TimerManager
class TimerManager {
var timerCount = 3600
static let sharedInstance = TimerManager()
var timer: Timer!
init() {
self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(update), userInfo: nil, repeats: true)
}
#objc func update() {
if(timerCount > 0){
let minutes = String(timerCount / 60)
let seconds = String(timerCount % 60)
timerCount -= 1
}
else {
self.timer?.invalidate()
}
}
}
// At view Controller you can access timerCount
print(TimerManager.sharedInstance.timerCount)

Timer.scheduledTimer Swift 3 pre-iOS 10 compatibility

I need to schedule a Timer for firing a function every second but I see that in Xcode 8 beta 3 the scheduledTimer is only available for iOS 10.
Is there any alternative for using the timer in iOS 9 or previous versions?
Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) in print("Hi!")})
Solved using
Timer.scheduledTimer(timeInterval: 1,
target: self,
selector: #selector(self.updateTime),
userInfo: nil,
repeats: true)
Run a timer with swift3,
var timer: Timer?
func startTimer() {
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.loop), userInfo: nil, repeats: true)
}
}
func stopTimer() {
if timer != nil {
timer?.invalidate()
timer = nil
}
}
func loop() {
let liveInfoUrl = URL(string: "http://192.168.1.66/api/cloud/app/liveInfo/7777")
let task = URLSession.shared.dataTask(with: liveInfoUrl! as URL) {data, response, error in
guard let data = data, error == nil else { return }
print(String(data: data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) ?? "aaaa")
}
task.resume()
}
Release the timer when you not use it.
Once scheduled on a run loop, the timer fires at the specified
interval until it is invalidated. A nonrepeating timer invalidates
itself immediately after it fires. However, for a repeating timer, you
must invalidate the timer object yourself by calling its invalidate()
method.
Here is sample code workable with compatibility:
if #available(iOS 10.0, *) {
Timer.scheduledTimer(withTimeInterval: 15.0, repeats: true){_ in
// Your code is here:
self.myMethod()
}
} else {
Timer.scheduledTimer(timeInterval: 15.0, target: self, selector: #selector(self.myMethod), userInfo: nil, repeats: true)
}
//Your method or function:
// MARK: - Method
#objc func myMethod() {
print("Hi, How are you.")
}
Swift 3
func runCode(in timeInterval:TimeInterval, _ code:#escaping ()->(Void))
{
DispatchQueue.main.asyncAfter(
deadline: .now() + timeInterval,
execute: code)
}
func runCode(at date:Date, _ code:#escaping ()->(Void))
{
let timeInterval = date.timeIntervalSinceNow
runCode(in: timeInterval, code)
}
func test()
{
runCode(at: Date(timeIntervalSinceNow:2))
{
print("Hello")
}
runCode(in: 3.0)
{
print("World)")
}
}
Updated for swift 3:
If you want to use Timer for some delay or any other purpose used below lines of code in your project;
// function defination:
func usedTimerForDelay() {
Timer.scheduledTimer(timeInterval: 0.3,
target: self,
selector: #selector(self.run(_:)),
userInfo: nil,
repeats: false)
}
func run(_ timer: AnyObject) {
print("Do your remaining stuff here...")
}
// function call:
self.usedTimerForDelay()
NOTE:- you can change the time interval as you want.
//Enjoy coding..!
Timer.scheduledTimer
Put it in the main thread.
You can use the following simple shim to provide the new block-based Timers to pre-iOS 10:
class TimerShim {
private var timer: Timer?
private let block: (Timer) -> Void
private init(timeInterval interval: TimeInterval, repeats: Bool, block: #escaping (Timer) -> Void) {
self.block = block
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(timerDidFire), userInfo: nil, repeats: repeats)
}
class func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: #escaping (Timer) -> Void) -> Timer {
return TimerShim(timeInterval: interval, repeats: repeats, block: block).timer!
}
#objc private func timerDidFire() {
block(timer!)
}
}
Usage example:
TimerShim.scheduledTimer(withTimeInterval: 5, repeats: false) { _ in
print("boom!")
}
The correct form is:
Timer.scheduledTimer(withTimeInterval: 2, repeats: false){_ in
"Here your code method"
}

Swift 2 - Timed Actions one second apart?

I'm trying to get output like so:
1 (then a one second delay)
Hello
2 (then a one second delay)
Hello
3 (then a one second delay)
Hello
But instead I get
1
2
3 (then a one second delay)
Hello
Hello
Hello
Here's my for loop invoking the NSTimer
var timer = NSTimer()
for i in 1...3 {
print("\(i)");
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(MainVPScreenViewController.printTest), userInfo: nil, repeats: false)
}
And here's the selector method:
func printTest() {
print("Hello")
}
Thanks in advance for any help you can provide
Try this solution without NSTimer:
var i = 1
func printHello() {
print(i)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
print("Hello")
i +=1
if i <= 3 {
printHello()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
printHello()
}
I need 2 NSTimers to do this, this is my approach
class ViewController: UIViewController {
var i = 1
override func viewDidLoad() {
super.viewDidLoad()
beginPrinting()
}
func beginPrinting() {
var timer2 = NSTimer()
if(i <= 100)
{
timer2 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printWithDelay), userInfo: nil, repeats: false)
}
}
func printWithDelay()
{
var timer = NSTimer()
print("\(i)");
i += 1
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(self.printTest), userInfo: nil, repeats: false)
}
func printTest() {
print("Hello")
beginPrinting()
}
}
Hope this helps you
Use timer with repeat to true. So in your view controller would be like this
var timer = NSTimer()
var counter = 0
var max = 10
let delay = 1 // in second
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self,
selector: #selector(self.printTest), userInfo: nil, repeats: true)
}
func printTest() {
counter += 1
print(counter)
print(hello)
if counter == maxNumber {
timer.invalidate()
}
}
This does it with repeat false, and is set up to be in a playground:
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
#objc class Foo: NSObject {
static var timer = NSTimer()
var i:Int
override init() {
Foo.timer = NSTimer()
i = 1
}
func schedule() {
print("\n\(i)");
i += 1
Foo.timer = NSTimer.scheduledTimerWithTimeInterval(1.0,
target: self,
selector: #selector(printTest),
userInfo: nil,
repeats: false)
}
#objc func printTest() {
print("Hello")
if i < 5 {
schedule()
}
}
}
let bar = Foo()
bar.schedule()

Resources