How to stop a timer after a function has been called - timer

I have a timer and a function. I want to invalidate the timer after the function has been completed. How would I go about this? the function is called showButton()

You will need to declare a timer variable globally to your class. Then initialize the timer when required and invalidate it when your showButton() function is called.
This is a small example.
class timerViewController: UIViewController {
var timer: Timer!
override func viewDidLoad() {
super.viewDidLoad()
print("Starting Timer")
timer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(timerCompleted), userInfo: nil, repeats: false)
}
#objc func timerCompleted(sender: Timer!){
print("Timer Completed")
}
func showButton(){
timer.invalidate()
}
}

Related

Swift appended items are not saved

Here, I declare myArray.
class AcikIlanlarViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myArray: [String] = []
Here, I call the function loadPage which modifies the array.
override func viewDidLoad() {
loadPage()
//when printed here myArray is nil
...
func loadPage() {
...
Alamofire.request(url, method: .get, parameters: parameters, encoding: JSONEncoding.default)
.validate { request, response, data in
...
let databases = try JSONDecoder().decode(responseData.self,from: json)
...
for i in 0...databases.orders.count-1 {
myArray.append(databases.orders[i].header!)
}
// when printed here I see the append has worked and myArray is not empty
}
As explained in comments, in viewDidLoad I call loadPage that appends certain stuff in myArray. Just after the append, I can see appended items. However, in viewDidLoad, just after the call to loadPage, myArray turns out to be empty. Why it is not saved?
Reload the table after the for loop
for i in 0...databases.orders.count-1 {
myArray.append(databases.orders[i].header!)
}
self.tableView.reloadData()
loadPage() calls an asynchronous method, so the func will finish before the operation is completed. Mostly, funcs that call async methods should have a completion handler…
override func viewDidLoad() {
loadPage {
self.tableView.reloadData()
}
}
func loadPage(completion: () -> Void) {
Alamofire.request(url, method: .get, parameters: parameters, encoding: JSONEncoding.default).validate { request, response, data in
let databases = try JSONDecoder().decode(responseData.self,from: json)
for i in 0...databases.orders.count-1 {
myArray.append(databases.orders[i].header!)
}
completion()
}
}

EXC_BREAKPOINT when trying to present modally from storyboard in completion handler

In my ViewController.swift I have this method which presents a viewcontroller modally after the animations for the boxes are completed:
#objc func sceneTapped(recognizer: UITapGestureRecognizer) {
let location = recognizer.location(in: sceneView)
let hitResults = sceneView.hitTest(location, options: nil)
if hitResults.count > 0 {
let result = hitResults[0]
let box = result.node
let fadeAction = SCNAction.fadeOut(duration: 1.0)
let rotateAction = SCNAction.rotate(by: CGFloat(Double.pi*2), around: SCNVector3(x: 0.5, y: 1.5, z: 0), duration: 1.0)
let groupActions = SCNAction.group([rotateAction, fadeAction])
if box.name == Present.left.rawValue || box.name == Present.middle.rawValue || box.name == Present.right.rawValue {
box.runAction(groupActions, completionHandler: presentWebView)
}
}
}
presentWebView uses the storyboard to instantiate the viewcontroller:
func presentWebView(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "webViewController")
self.present(vc, animated: true, completion: nil)
}
But every time the app crashes on self.present(vc, animated: true, completion: nil) with EXC_BREAKPOINT (code=1, subcode=0x1a1579b50)
HOWEVER, when I don't present the view controller in the completion hander, i.e.:
#objc func sceneTapped(recognizer: UITapGestureRecognizer) {
presentWebView()
}
This works completely fine
So I don't understand why presenting a view controller in a completion handler causes a crash.
Any help would be greatly appreciated!
the SCNAction completion handler will not be called in the main thread.
Per UIKit guidelines your UI code needs to run on the main thread. You should be able to do that using DispatchQueue.main.async { ... }

Continue Countdown Timer in Today Extension - Swift 3

How can I continue a timer in Today extension when the user swipes back up and closes the view? Every time I close my view, my label resets back to five minutes. The timer doesn't necessarily have to animate while the view is disappeared, but when the view loads again I would like the animation showing the updated time that passed while the view was gone to be smooth and fast. How can I keep it counting down in the background? What's the most efficient and logical way to do this? Thank you.
Here is my code thus far:
class TodayViewController: UIViewController, NCWidgetProviding {
var backgroundTaskIdentifier: UIBackgroundTaskIdentifier?
#IBOutlet weak var timerLabel: UILabel!
var counter = 300
var newCount = Int()
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateInfo), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: RunLoopMode.commonModes)
}
func updateInfo(){
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
self.timerLabel.text = formatter.string(from: TimeInterval(self.counter))
counter -= 1
print(counter)
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
completionHandler(NCUpdateResult.newData)
}
override func viewDidDisappear(_ animated: Bool) {
counter -= 1
}
}

Using Swift 3 Stopping a scheduledTimer, Timer continue firing even if timer is nil

We call startTimer function to start a timer. When we wanted to stop it we call stopTimerTest function but after we called stopTimer function the timerTestAction keeps firing. To check the timer condition we used print and print in timerActionTest returns nil.
var timerTest: Timer? = nil
func startTimer () {
timerTest = Timer.scheduledTimer(
timeInterval: TimeInterval(0.3),
target : self,
selector : #selector(ViewController.timerActionTest),
userInfo : nil,
repeats : true)
}
func timerActionTest() {
print(" timer condition \(timerTest)")
}
func stopTimerTest() {
timerTest.invalidate()
timerTest = nil
}
Try to make the following changes to your code:
First, you have to change the way you declare timerTest
var timerTest : Timer?
then in startTimer before instantiating check if timerTest is nil
func startTimer () {
guard timerTest == nil else { return }
timerTest = Timer.scheduledTimer(
timeInterval: TimeInterval(0.3),
target : self,
selector : #selector(ViewController.timerActionTest),
userInfo : nil,
repeats : true)
}
Finally in your stopTimerTest you invalidate timerTest if it isn't nil
func stopTimerTest() {
timerTest?.invalidate()
timerTest = nil
}
Most likely you've called startTimer twice without calling stopTimerTest. If you do that, you'll lose your pointer to the original timer and never be able to invalidate it.
The typical approach is to manage invalidation as a part of setting:
var timerTest : Timer? = nil {
willSet {
timerTest?.invalidate()
}
}
Then stopping is just setting to nil:
func stopTimerTest() {
timerTest = nil
}
Make sure when you call StartTimer it is nil and if you call StartTimer twice without calling StopTimer. You will lose your original pointer and you can't stop it.
var timer : Timer? = nil {
willSet {
timer?.invalidate()
}
}
Start and Stop timer like ...
func startTimer() {
stopTimer()
guard self.timer == nil else { return }
self.timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(self.fetchData), userInfo: nil, repeats: true)
}
func stopTimer() {
guard timer != nil else { return }
timer?.invalidate()
timer = nil
}
Check, are you really call stopTimerTest(), because timerTest.invalidate() is correct for stopping timer.
func stopTimerTest() {
print("stopTimer")
timerTest.invalidate()
}

SWIFT - Multiple NSTimers

I have run in to a slight problem when trying to manage multiple NSTimers in SWIFT. No matter what I Try it will only ever invalidate the last timer I create. But I need to be able to invalidate any of the timers by choice.
Is there some way to create a reference ID that I could then use to select the specific NSTimer I want to invalidate? Any help would be great.
here is a snippet of my code.
import UIKit
var myTimer:NSTimer!
class TimerManager: NSObject {
}
public class Timer {
// each instance has it's own handler
private var handler: (timer: NSTimer) -> () = { (timer: NSTimer) in }
public class func start(duration: NSTimeInterval, repeats: Bool, handler:(timer: NSTimer)->()) {
var t = Timer()
t.handler = handler
myTimer = NSTimer.scheduledTimerWithTimeInterval(duration, target: t, selector: "processHandler:", userInfo: nil, repeats: repeats)
}
#objc private func processHandler(timer: NSTimer) {
self.handler(timer: timer)
}
}
class countdown{
//handles the countdown for the timer and invalidates when it reaches 0
var y = 0
func begin(length:Int) {
y = length
let delta = 1
let delay = 1.0
Timer.start(delay, repeats: true) {
(t: NSTimer) in
self.y -= delta
println(self.y)
if (self.y <= 0) {
t.invalidate()
}
}
}
func end () {
println("timer stopped")
myTimer.invalidate()
}
}
And I create the timer like so:
countdownTimer.begin(120) //time length in seconds ... 120 = 2 mins
To stop timer:
countdownTimer.end()
You may create a dictionary retaining NSTimer objects. Note that timerManager needs to be defined at global scope. Hope it shed light.
class TimerManager {
var _timerTable = [Int: NSTimer]()
var _id: Int = 0
/*! Schedule a timer and return an integer that represents id of the timer
*/
func startTimer(target: AnyObject, selector: Selector, interval: NSTimeInterval) -> Int {
var timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: target, selector: selector, userInfo: nil, repeats: true)
_id += 1
_timerTable[_id] = timer
return _id
}
/*! Stop a timer of an id
*/
func stopTimer(id: Int) {
if let timer = _timerTable[id] {
if timer.valid {
timer.invalidate()
}
}
}
/*! Returns timer instance of an id
*/
func getTimer(id: Int) -> NSTimer? {
return _timerTable[id]
}
}
// This needs to be delcared at global scope, serving as "singleton" instance of TimerManager
let timerManager = TimerManager()
Following code creates a new timer.
var aTimer = timerManager.startTimer(self, selector: Selector("timerFunction"), interval: 1)
To stop the timer, simply pass id to stopTimer(id: Int).
/* Code ... */
timerManager.stopTimer(aTimer)
Also note getTimer method returns the actual instance with an id.
Regards

Resources