The timer is terminated when the view changes - swift

I am not a native English speaker and I am writing with a translator.
I want you to understand if the context is strange.
My Question is,
When the button is pressed, the timer operates, but when the view is switched, the timer is not operating.
I wonder how the timer can be maintained even if the view changes.
var time : Int = 0
var onehour : Int = 60
var timer = Timer()
#IBAction func pressSuncream(_ sender: Any) { // Suncream 버튼
useSuncream.setImage(UIImage(named: "test"), for: UIControlState.normal)
if #available(iOS 10.0, *) {
let content = UNMutableNotificationContent()
content.title = "hello"
content.body = "timer"
content.badge = 1
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3600, repeats: false)
let request = UNNotificationRequest(identifier: "timerdone", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(QuestView.updatetime), userInfo: nil, repeats: true)
} else {
// Fallback on earlier versions
}
}
func updatetime() {
time += 1
if time == 60 {
time = 0
onehour -= 1
DispatchQueue.main.async {
self.suncreamTime.text = String(self.onehour)
}
}
}

when you switch to the mainView,you can store the value of onehour in the UserDefaults at the override func viewWillDisappear(_ animated: Bool) { }.
when you into the timerView,you can get the value at the override func viewWillAppear(_ animated: Bool) {}
eg:
override func viewWillDisappear(_ animated: Bool) {
let userDefault = UserDefaults.standard
userDefault.set(onehour, forKey: "onehour")
}
override func viewWillAppear(_ animated: Bool) {
if let onehour = UserDefaults.standard.value(forKey: "onehour") {
self.onehour = onehour as! String
}
}

One way that you could do this, is to make the timer variable global. This is probably a "dirty" way to do it, and if there really are terrible ramifications of doing this, someone let me know and I'll take this down.
Essentially all you would need to do is define it outside of any class like so:
var timer: NSTimer?
class MyClass: UIViewController {
...
}
This way you can access it from any file. I would say that you are generally discouraged from doing this unless you have to, so it's more of a last-resort option.

Related

Repeating timer with New Time Intervals

Hi this is my first app and I have been trying to figure out how to do this simple task but am getting overloaded with information and I have a feeling there may be an easier way to do it.
I am trying to build a random word generator that will select from an array of words at random time intervals. The time needs to change after the timer fires. The timer can keep running the whole time the app is on.
here is my code so far:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#objc weak var paraTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
let randomNum = Int.random(in: 1..<5)
paraTimer = Timer.scheduledTimer(timeInterval: TimeInterval(randomNum), target: self, selector: #selector(runTimedCode), userInfo: nil, repeats: true)
}
/*
This invalidates my timer, not sure if I need to start a new timer now or if I can reset this?
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
paraTimer?.invalidate()
*/
#objc func runTimedCode() {
let array = ["Apple", "Banana", "Pear" ]
label.text = array.randomElement()
}
}
So the issue that I need to fix is it will select a random number from 1-5 and then not select a new number and just keep running every x seconds. Also should I invalidate the timer or leave that off?
Thank you. Like I said, this is my first time working on an app that doesn't just follow a straight tutorial.
To restart a timer with different intervals you have to recreate the timer.
First of all declare the timer strong and #objc is not needed
var paraTimer: Timer?
I recommended to create an extra method. Declare the timer non-repeating and use the block based API of Timer. The timer restarts itself.
func restartTimer() {
let randomNum = Int.random(in: 1..<5)
paraTimer = Timer.scheduledTimer(timeInterval: TimeInterval(randomNum), repeats: false) { [weak self] _ in
let array = ["Apple", "Banana", "Pear" ]
self?.label.text = array.randomElement()
self?.restartTimer()
}
}
Start the timer in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
restartTimer()
}
and stop it in viewDidDisappear
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
paraTimer?.invalidate()
paraTimer = nil
}

How to count Timer on Swift

I'm a newbie in swift and I tried to make a Timer. It should normally count the seconds and print them in the debugger.
I tried this:
var timer = Timer()
#IBAction func killTimer(_ sender: AnyObject) {
timer.invalidate()
}
#objc func processTimer() {
print("This is a second")
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(ViewController.processTimer), userInfo: nil, repeats: true)
}
I don't know how the timer count seconds.. With this code i get an fail message:
#objc func processTimer() {
print("This is second \(Timer + 1)")
}
Thanks for your help.
A
You need a counter variable which is incremented every time the timer fires.
Declare the variable Timer as optional to invalidate the timer reliably (only once).
var timer : Timer?
var counter = 0
#IBAction func killTimer(_ sender: AnyObject) {
timer?.invalidate()
timer = nil
}
#objc func prozessTimer() {
counter += 1
print("This is a second ", counter)
}
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval:1, target:self, selector:#selector(prozessTimer), userInfo: nil, repeats: true)
}
You need to start your timer, you have only initialised it in your code so in viewDidLoad add
timer.fire()
I am not sure what you want to print but if it is a timestamp then you could do add a property to your class
let formatter = DateFormatter()
and configure it to show time in seconds and milliseconds
formatter.dateFormat = "ss.SSS" //in viewDidLoad
and use it in your print statement
print("This is a second \(formatter.string(from(Date())")
If you want to print the time that passed use this (If that's what you want):
print("time passed: \(Date().timeIntervalSince1970 - timer.fireDate.timeIntervalSince1970)")

Zero amplitude and frequency from audiokit tracker on device but works on simulator

AudioKit
I have two view controllers that are doing different audio related function. The first view viewController, needs to record audio and also do real-time processing of the amplitude and frequency of the sound being recorded. The second view controller needs to play the recorded audio, and do some stuff with the playhead.
My problem is that when I go back and forth a few times between the two screens on my iphone 6 device, the amplitude and frequency of the tracker only equal 0. This behavior does not happen on my simulator. Similarly, I've tried different combinations of starting + stopping the tracker and/or the audio engine at different points in the workflow but then I just get crashes along the lines of https://github.com/audiokit/AudioKit/issues/643. One thing I have been trying to figure out is how to make a singleton instance of the aukiokit so I can only start the engine once, but I can't get it from examples.
Here are the key sections of code that use the audiokit, microphone and tracker:
First view controller in the workflow
class ViewController: UIViewController {
#IBAction func analyzeSpeechAction(_ sender: Any) {
//Audiokit.stop()
stopTimers()
let vc = AnalysisViewController(nibName: "AnalysisViewController", bundle: nil)
print("ANALYZING")
do {
try player.reloadFile()
} catch { print("Errored reloading.") }
let recordedDuration = player != nil ? player.audioFile.duration : 0
if recordedDuration > 0.0 {
recorder.stop()
}
tracker.stop()
navigationController?.pushViewController(vc, animated: true)
}
func stopTimers(){
if timerTest != nil {
timerTest!.invalidate()
timerTest = nil
}
if timer != nil {
timer!.invalidate()
timer = nil
}
}
var mic: AKMicrophone!
var tracker: AKFrequencyTracker!
var silence: AKBooster!
var mainMixer: AKMixer!
var timerTest : Timer?
var micMixer: AKMixer!
var recorder: AKNodeRecorder!
var player: AKAudioPlayer!
var tape: AKAudioFile!
var micBooster: AKBooster!
override func viewDidLoad() {
super.viewDidLoad()
AKAudioFile.cleanTempDirectory()
AKSettings.bufferLength = .medium
do {
try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
} catch {
AKLog("Could not set session category.")
}
AKSettings.defaultToSpeaker = true
AKSettings.audioInputEnabled = true
mic = AKMicrophone()
tracker = AKFrequencyTracker(mic)
tracker.start()
silence = AKBooster(tracker, gain: 0)
micMixer = AKMixer(mic)
micBooster = AKBooster(micMixer)
// Will set the level of microphone monitoring
micBooster.gain = 0
recorder = try? AKNodeRecorder(node: micMixer)
if let file = recorder.audioFile {
player = try? AKAudioPlayer(file: file)
}
mainMixer = AKMixer(micBooster)
do {
try recorder.record()
} catch { print("Errored recording.") }
AudioKit.output = mainMixer
}
viewDidAppear(){
AudioKit.stop()
AudioKit.output = silence
AudioKit.start()
if timer == nil{
timer = Timer.scheduledTimer(timeInterval: 1, target:self,
selector: #selector(ViewController.updateCounter), userInfo: nil, repeats: true)
}
}
#objc func updateUI() {
frame = frame + 1
print("AMP")
print(tracker.amplitude)
print(tracker.frequency)
}
}
Second View Controller
class AnalysisViewController: UIViewController {
#IBOutlet weak var barChartView: LineChartView!
#IBAction func recordSpeechNavigation(_ sender: Any) {
let vc = ViewController(nibName: "ViewController", bundle: nil)
//AudioKit.stop()
player.stop()
navigationController?.pushViewController(vc, animated: true)
}
#IBAction func playButtonAction(_ sender: UIButton) {
playAudio()
}
override func viewDidLoad() {
super.viewDidLoad()
//move to completion handler
player.completionHandler = {() -> Void in
self.playerStatusButton.setTitle("Play", for: .normal)
self.timerTest!.invalidate()
self.relativePlayheadPosition = 0.0
self.movePlayhead()
print("complete")
}
}
}
I kind of mashed up a bunch of examples to get this far so I apologize if I am improperly using some of the AudioKit methods. The key behavior occurs between viewDidLoad(), viewDidAppear() and updateUI() of the first view and recordSpeechNavigation() of the second view.
Does anyone have any insight into what might be happening or how I can properly implement this stuff?

Timer counts instantly instead of respecting interval

Im trying to make a countdown timer. everything works fine except that my timer does not count at regular intervals (1sec); instead it counts all the way down instantly giving me 0 every time. did a lot of search without luck. All examples I could find show similar timeInterval parameter.
var timer = Timer()
var remainingTime = 120
#objc func timerCount () {
if remainingTime > 0 {
while remainingTime > 0 {
remainingTime -= 1
timerLabel.text = String(remainingTime)
print(remainingTime)
}
} else {
timer.invalidate()
}
}
#IBAction func pauseButton(_ sender: Any) {
timer.invalidate()
}
#IBAction func playButton(_ sender: Any) {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerCount), userInfo: nil, repeats: true)
}
The reason your code is not working is that you have put an unnecessary while loop in your timerCount() method. You don't need to do this. Your timer will fire this method after each time interval. At very first call this while loop make your remainingTime to 0. This is why you are instantly getting 0 every time.
You just need to remove that while loop.
Can You try like this
var timer = Timer()
var remainingTime = 120
#objc func timerCount () {
let date = NSDate()
let nowdate = UserDefaults.standard.object(forKey: "NowDate") as! Date
let miniute = nowdate.timeIntervalSince(date as Date)
print(Int(miniute))
if (Int(miniute) == 0) {
timer.invalidate()
UserDefaults.standard.removeObject(forKey: "NowDate")
} else {
timerLabel.text = String(Int(miniute))
}
}
#IBAction func pauseButton(_ sender: Any) {
timer.invalidate()
UserDefaults.standard.removeObject(forKey: "NowDate")
}
#IBAction func playButton(_ sender: Any) {
let CurrentDate = NSDate()
let NowDate = CurrentDate.addingTimeInterval(TimeInterval(remainingTime))
UserDefaults.standard.set(NowDate, forKey: "NowDate")
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.timerCount), userInfo: nil, repeats: true)
}

Having UILabel update based on Audio Player current time

I am attempting to have my UILabel update based on the duration of what the AudioPlayer current time is. I don't want to use a timer, because within my app people will be recording themselves with small durations & the half seconds are important, so I wouldn't really want to use timer(). Instead what I am attempting to do is just have the label update based on what the current time is automatically. I can't however seem to figure it out...
Here is how I am going about it, but clearly I am not doing it correctly
var audioPlayer : AVAudioPlayer!
var audioTime = UILabel() {
didSet {
do {
try audioPlayer = AVAudioPlayer(contentsOf: audioURL!)
let currentTime = Int(audioPlayer.currentTime)
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
audioTime.text = (NSString(format: "%02d:%02d", minutes,seconds) as String)
} catch {
}
}
}
This might help you! Connect Outlet to showTimeLbl and try.
class ViewController: UIViewController {
#IBOutlet weak var showTimeLbl: UILabel!
var timeCount:Int = 0
var timer:Timer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if(timer != nil)
{
timer.invalidate()
}
timer = Timer(timeInterval: 1.0, target: self, selector: #selector(ViewController.timerDidFire), userInfo: nil, repeats: true)
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
}
func timerDidFire()
{
timeCount += 1
showTimeLbl.text = NSString(format: "%02d:%02d:%02d", timeCount/3600,(timeCount/60)%60,timeCount%60) as String
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
timer?.invalidate()
timer = nil
}