How do I switch my play button to a pause button in Swift? - swift

working on my first stopwatch app.
I currently have a play button, pause button, and stop button.
I'd like to combine the play and pause button so that they switch back and forth.
My code looks like this:
var timer = NSTimer()
var count = 0
func updateTime() {
count++
time.text = "\(count)"
}
#IBAction func pauseButton(sender: AnyObject) {
timer.invalidate()
}
#IBOutlet weak var time: UILabel!
#IBAction func stopButton(sender: AnyObject) {
timer.invalidate()
count = 0
time.text = "0"
}
#IBAction func playButton(sender: AnyObject) {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true)
}
Any help is appreciated.

Try adding booleans. See my code below.
#IBOutlet weak var label: UILabel!
var time = NSTimer()
var count = 0
var running = false
func result (){
count++
label.text = String(count)
println(count)
}
#IBAction func playpause(sender: AnyObject) {
if running == false {
time = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
running = true }
else {
time.invalidate()
running = false
}
}
Hope this helps!

You'll have a variable bound to the button, something like this:
#IBOutlet var thePlayPauseButton : UIButton!
This button will be linked with some action:
#IBAction func togglePlayPauseButton (button: UIButton) {
// If we are 'paused', then play:
if button.titleLabel!.text == "Pause" {
button.titleLabel!.text = "Play"
// do actual play ...
timer = NSTimer.scheduledTimerWithTimeInterval (1,
target: self,
selector: Selector("updateTime"),
userInfo: nil,
repeats: true)
}
else if button.titleLabel!.text == "Play" {
button.titleLabel!.text = "Pause"
// do actual pause ...
timer.invalidate()
}
else { /* error */ }
}
Of course, structurally you can use a switch//case and you can perform the toggle behavior by calling your preexisting pause and play methods.

I know this post is somewhat dated, but I was working with the same problem and I came up with a slightly different answer and wanted to share it to help others. Here is what I came up with in toggling the pause and play buttons.
class ViewController: UIViewController {
var time = NSTimer()
var seconds = 0
var running = false
func timer() {
seconds++
timeLabel.text = "\(seconds)"
}
func playing() {
time = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timer"), userInfo: nil, repeats: true)
running = true
}
func pausing() {
time.invalidate()
running = false
}
#IBOutlet weak var timeLabel: UILabel!
#IBAction func stopButton(sender: AnyObject) {
time.invalidate()
seconds = 0
timeLabel.text = "0"
}
#IBAction func pausePlayToggleButton(sender: AnyObject) {
if running == false {
return playing()
} else {
return pausing()
}
}
I had both a pause and play button and I essentially took their effects and placed them in functions and used them as return values for a single button.

Related

Error in Swift 5 with Xcode when creating a timer out of a label

I'm using Swift 5 in Xcode to code an app. On one view controller, I am creating a timer, which counts from 20 minutes down to 0. I have what I think is a successful code, but it throws back one error. In the line
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)
it gives an error saying Type 'SkippingViewController' has no member 'updateTimer' (SkippingViewController is the name of the view controller for the page of the app my timer is on)
How can I resolve this issue?
import UIKit
class SkippingViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var startWorkoutButton: UIButton!
#IBOutlet weak var pauseWorkoutButton: UIButton!
var timer = Timer()
var counter = 20.00
var isRunning = false
override func viewDidLoad() {
super.viewDidLoad()
timeLabel.text = "\(counter)"
startWorkoutButton.isEnabled = true
pauseWorkoutButton.isEnabled = false
// Do any additional setup after loading the view.
}
#IBAction func startWorkoutButtonDidTouch(_ sender: Any) {
if !isRunning {
timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(SkippingViewController.updateTimer), userInfo: nil, repeats: true)
startWorkoutButton.isEnabled = false
pauseWorkoutButton.isEnabled = true
isRunning = true
}
}
#IBAction func pauseWorkoutButtonDidTouch(_ sender: Any) {
func updateTimer() {
counter -= 0.01
timeLabel.text = String(format: "%.01f", counter)
}
}
}
Your problem is, that there is no method called 'updateTimer' in SkippingViewController.swift. You falsely put the method inside of the method 'pauseWorkoutButtonDidTouch'. In order to resolve the error insert the following code into SkippingViewController.swift:
#objc func updateTimer() {
counter -= 0.01
timeLabel.text = String(format: "%.01f", counter)
}

Swift: another timer starting when application enters background

I've been struggling to figure this out for a few days: I need to create a timer that the user can't kill, so once they start it, even if they kill the app or it enters background, it will pick up where it left off, and to achieve this I am saving the date when the app terminated and then calculating the difference, so that part works fine.
The problem I keep running into however is that a second timer seems to start if I minimise the app and bring it back. I'm not sure how timers are managed in the background, but nothing of what I tried (calling timer.invalidate() when applicationWillResignActive gets called, calling it in deinit() ) seems to work. The behaviour I see after this is that the timer will count like this: 80 - 78 - 79 - 76 - 77..
There's also a problem where the timer will sometime run past the time it's supposed to run for after killing the app, but I can't find the exact cause for that because it doesn't always happen.
Any idea what I'm doing wrong?
Thanks a lot.
class Focus: UIViewController {
// MARK: Variables
var timer = Timer()
let timeToFocus = UserDefaults.standard.double(forKey: "UDTimeToFocus")
let currentFocusedStats = UserDefaults.standard.integer(forKey: "UDFocusStats")
// MARK: Outlets
#IBOutlet weak var progress: KDCircularProgress!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var focusTimeLabel: UILabel!
#IBOutlet weak var stepNameLabel: UILabel!
#IBOutlet weak var focusAgain: UIButton!
#IBOutlet weak var allDone: UIButton!
#IBOutlet weak var help: UIButton!
#IBOutlet weak var dottedCircle: UIImageView!
// MARK: Outlet Functions
#IBAction func helpTU(_ sender: Any) { performSegue(withIdentifier: "ToFocusingHelp", sender: nil) }
#IBAction func helpTD(_ sender: Any) { help.tap(shape: .rectangle) }
#IBAction func allDoneTU(_ sender: Any) {
UserDefaults.standard.set(false, forKey: "UDFocusIsRunning")
UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
completeSession()
hero(destination: "List", type: .zoomOut)
}
#IBAction func allDoneTD(_ sender: Any) { allDone.tap(shape: .rectangle) }
#IBAction func focusAgainTU(_ sender: Any) {
UserDefaults.standard.set(currentFocusedStats + Int(timeToFocus), forKey: "UDFocusStats")
UserDefaults.standard.set(true, forKey: "UDShouldStartFocus")
initFocus()
}
#IBAction func focusAgainTD(_ sender: Any) { focusAgain.tap(shape: .rectangle) }
// MARK: Class Functions
#objc func initFocus() {
var ticker = 0.0
var angle = 0.0
var duration = 0.0
if UserDefaults.standard.bool(forKey: "UDShouldStartFocus") == true {
UserDefaults.standard.set(Date(), forKey: "UDFocusStartDate")
UserDefaults.standard.set(false, forKey: "UDShouldStartFocus")
ticker = timeToFocus
duration = timeToFocus
angle = 0.0
print("starting")
} else {
let elapsedTime = difference(between: UserDefaults.standard.object(forKey: "UDFocusStartDate") as! Date, and: Date())
let timeLeft = timeToFocus - elapsedTime
ticker = timeLeft
duration = timeLeft
angle = elapsedTime / (timeToFocus / 360)
}
// Timer
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
if ticker > 0 {
self.timeLabel.text = "\(Int(ticker))s"
ticker -= 1
}
}
timer.fire()
// Progress Circle
progress.animate(fromAngle: angle, toAngle: 360, duration: duration) { completed in
if completed { self.completeSession() }
}
// UI Changes
allDone.isHidden = true
focusAgain.isHidden = true
help.isHidden = false
}
func completeSession() {
// The timer gets fired every time, but this will invalidate it if it's complete
timer.invalidate()
timeLabel.text = "Done"
help.isHidden = true
allDone.isHidden = false
focusAgain.isHidden = false
}
// MARK: viewDidLoad
override func viewDidLoad() {
initFocus()
allDone.isHidden = true
focusAgain.isHidden = true
if timeToFocus < 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60)) minutes" }
else if timeToFocus == 3600 { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hour" }
else { focusTimeLabel.text = "Focusing for \(Int(timeToFocus/60/60)) hours" }
stepNameLabel.text = UserDefaults.standard.string(forKey: "UDSelectedStep")
// This resumes the timer when the user sent the app in the background.
NotificationCenter.default.addObserver(self, selector: #selector(self.initFocus), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.fadeProgress), name: NSNotification.Name(rawValue: "WillEnterForeground"), object: nil)
}
#objc func fadeProgress(){
// This function is called both when the view will enter foreground (for waking the phone or switching from another app) and on viewWillAppear (for starting the app fresh). It will fade the progress circle and buttons to hide a flicker that occurs.
timeLabel.alpha = 0
dottedCircle.alpha = 0
progress.alpha = 0
allDone.alpha = 0
focusAgain.alpha = 0
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2, execute: {
UIButton.animate(withDuration: 0.5, animations: {
self.timeLabel.alpha = 1
self.dottedCircle.alpha = 1
self.progress.alpha = 1
self.allDone.alpha = 1
self.focusAgain.alpha = 1
})
})
}
// MARK: viewWillAppear
override func viewWillAppear(_ animated: Bool) { fadeProgress() }
}
It seems the problem is that you create a local timer variable inside initFocus() but you call invalidate inside completeSession for another timer defined there:
class Focus: UIViewController {
// MARK: Variables
var timer = Timer()

AVAudioplayer no resetting on viewDidAppear

The idea is simple and I do not think that the question has been asked in the past.
I want to build a simple mp3 player.
some songs displayed in a collection view the user selects a song
segue to another view with options to play, pause or stop only issue
is when you go back to the home screen to select a new song with the
current still playing. It is impossible to deactivate the current
player. When you need to play the 2 songs, the 2 are playing together
I have tried a lot of things
- create a new instance of player (player = AVAudioPlayer())
- player.pause() and player.play()
I do not see what I am doing wrong really.
this is my code :
import UIKit
import AVFoundation
class LecteurViewController: UIViewController {
var chansonSelected: Chanson? = nil
var lecteur:AVAudioPlayer = AVAudioPlayer()
var timer1 = Timer()
var timer2 = Timer()
#IBOutlet weak var dureeChansonSlider: UISlider!
#IBOutlet weak var chansonImageView: UIImageView!
#IBOutlet weak var chansonVolumeSlider: UISlider!
#IBOutlet weak var debutLabel: UILabel!
#IBOutlet weak var finLabel: UILabel!
#IBAction func stopMusicAction(_ sender: UIBarButtonItem) {
var player = AVAudioPlayer()
lecteur.stop()
LecteurManager.isActive = false
}
#IBAction func pauseMusicAction(_ sender: UIBarButtonItem) {
var player = AVAudioPlayer()
lecteur.pause()
LecteurManager.isActive = false
}
#IBAction func jouerMusicAction(_ sender: UIButton) {
if LecteurManager.isActive {
changeSong()
print("lecteur déjà en cours")
} else {
var player = AVAudioPlayer()
lecteur.play()
}
print(LecteurManager.isActive )
LecteurManager.isActive = true
}
func changeSong() {
lecteur.stop()
//lecteur = AVAudioPlayer()
jouerLecteurMp3()
print(chansonSelected!)
lecteur.play()
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
jouerLecteurMp3()
}
func configureView() {
self.title = (chansonSelected!.titre!).capitalized
chansonImageView.image = UIImage(named: "\(chansonSelected!.image).jpgs")
//formatter 'back' button
let backBtn = UIBarButtonItem(title: "< Playlist", style: .plain, target: self, action: #selector(LecteurViewController.reset(_sender:)))
self.navigationItem.leftBarButtonItem = backBtn
self.navigationController?.navigationBar.tintColor = UIColor.white
//contrôler volume chanson
chansonVolumeSlider.addTarget(self, action: #selector(LecteurViewController.ajusterVolume(_ :)), for: UIControlEvents.valueChanged)
//contrôler durée chanson
dureeChansonSlider.addTarget(self, action: #selector(LecteurViewController.ajusterDurée(_ :)), for: UIControlEvents.valueChanged)
updateUI()
}
func updateUI() {
//indiquer position chanson
timer1 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(LecteurViewController.mettreAJourDurée), userInfo: nil, repeats: true)
//afficher durée chanson
timer2 = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(LecteurViewController.afficherDurée), userInfo: nil, repeats: true)
}
func reset(_sender:UIBarButtonItem) {
self.navigationController?.popViewController(animated: true)
}
func ajusterVolume(_ sender:UISlider) {
//print("volume ajusté \(chansonVolumeSlider.value)")
lecteur.volume = chansonVolumeSlider.value
}
func ajusterDurée(_ sender:UISlider) {
lecteur.currentTime = TimeInterval(dureeChansonSlider.value)
}
func mettreAJourDurée() {
dureeChansonSlider.value = Float(lecteur.currentTime)
}
func afficherDurée() {
print("durée actuelle: \(lecteur.duration - lecteur.currentTime)")
debutLabel.text = retournerPositionActuelle()
finLabel.text = retournerDureeTotal()
}
func retournerPositionActuelle() -> String {
let seconds = Int(lecteur.currentTime) % 60
let minutes = (Int(lecteur.currentTime) / 60) % 60
return String(format: "%0.2i:%0.2i", minutes, seconds)
}
func retournerDureeTotal() -> String {
let seconds = Int(lecteur.currentTime) % 60
let minutes = (Int(lecteur.currentTime) / 60) % 60
return String(format: "%0.2i:%0.2i", minutes, seconds)
}
func jouerLecteurMp3() {
let chanson = "bensound-\(chansonSelected!.titre!)"
let fichierMp3 = Bundle.main.path(forResource: chanson, ofType: "mp3")
do {
try lecteur = AVAudioPlayer(contentsOf: URL(string: fichierMp3!)!)
dureeChansonSlider.maximumValue = Float(lecteur.duration)
} catch {
print("erreur lecture mp3")
}
}
}
Try this:
func reset(_sender:UIBarButtonItem)
{
self.navigationController?.popViewController(animated: true)
lecteur.stop()
}

Timer scope issue with #selector

I'm stuck with getting this into the right scope. I'm sure its something super simple but I'm banging my head against a wall with it. any answers i'm finding are in earlier version of swift so im struggling to understand how to solve this
My current issue is trying to get the timer initialised correctly and counting. the "selector" is causing the most issues. the rest i'm sure ill be able to figure out afterwards
code is as follows.
#IBOutlet weak var shortTimerLabel: UILabel!
#IBOutlet weak var longTimerLabel: UILabel!
var seconds = 60 //This variable will hold a starting value of seconds. It could be any amount above 0.
var timer = Timer()
var isTimerRunning = false //This will be used to make sure only one timer is created at a time.
#IBAction func longpressed(_ gestureRecognizer: UILongPressGestureRecognizer) {
shortTimerLabel.text = "longPressed"
}
#IBAction func tappedShortTimer(_ gestureRecognizer: UITapGestureRecognizer) {
shortTimerLabel.text = "ShortPressed"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
func runTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}
func updateTimer() {
seconds += 1 //This will decrement(count down)the seconds.
shortTimerLabel.text = "\(seconds)" //This will update the label.
}
}
im trying to create a stopwatch which can be controlled using gestures. short pressed on label for stop/start and long press to reset time.
In your updateTimer() method, the first line should read seconds -= 1 instead (if you want to count down).
Also, you may want to update your updateTimer() method like this:
func updateTimer() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
isTimerRunning = false
}
shortTimerLabel.text = String(describing: seconds)
}
An other issue here is that you added your runTimer() and updateTimer() method to the wrong place. You should not add them inside your viewDidLoad method.
Your final code would look like this:
var seconds = 60
var timer = Timer()
var isTimerRunning = false
#IBAction func longpressed(_ gestureRecognizer: UILongPressGestureRecognizer) {
resetTimer()
}
#IBAction func tappedShortTimer(_ gestureRecognizer: UITapGestureRecognizer) {
stopStartTimer()
}
override func viewDidLoad() {
super.viewDidLoad()
// ...
}
func stopStartTimer() {
if !isTimerRunning {
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
isTimerRunning = true
} else {
timer.invalidate()
isTimerRunning = false
}
}
func updateTimer() {
seconds -= 1
if seconds == 0 {
timer.invalidate()
isTimerRunning = false
}
shortTimerLabel.text = String(describing: seconds)
}
func resetTimer() {
if isTimerRunning {
seconds = 60
timer.invalidate()
isTimerRunning = false
stopStartTimer()
}
}
The selector should be given in the form #selector(ViewController.updateTimer)
You shouldn't declare functions in the viewDidLoad but outside
You only set the timer in the longpressed function
For stoping it is timer.invalidate()

Why does Xcode say "expected declaration" with an arrow pointing to the word "timer"? [duplicate]

This question already has an answer here:
Expected Declaration Error using Swift
(1 answer)
Closed 5 years ago.
I get an error on the line below func pauseAction that says "expected declaration" and there is an arrow pointing to the word "timer"
var timeLeft = 10
var timer = Timer()
var score: Int = 0 {
didSet {
scoreLabel.text = "\(score)"
}
}
#IBOutlet var scoreLabel: UILabel!
#IBAction func tapButton(_ sender: Any) {
score += 1
}
#IBAction func pauseAction(_ sender: Any) {
}
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: Selector(("updateTimer")), userInfo: nil, repeats: true)
func updateTimer() {
timeLeft -= 1
timeLabel.text = String(timeLeft)
if timeLeft == 0 {
timer.invalidate()
}
}
In this case expected declaration means the code must be inside a method / function for example
func startTimer()
{
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
}