How to capture the elapsed time when a timer is paused - ios

I have put together a count down timer so the user can recored their practice seasons. I have it working fine except for one part. The user is allowed to pause the session. The problem with the code thus far, is that the timer code pauses but the time keeps running.
For example, if the user sets the timer for 5 minutes and taps the start button, then midway through the user taps pauses and waits 2 minutes (which means the elapsed time is now 7 minutes) the practice time presented when the timer ends is 7 minutes instead fo 5 minutes. If the user cancels the session it's fine but only because I'm setting a hard start and end time.
My question is how do I capture the practice time if the user pauses the timer?
Here's an explanation of the UI:
The timerLabel contains the running time
The minutesLabel shows the number of minutes the user set the timer to
The hoursLabel shows the number of hours the user set the timer to
There are two sliders. One for setting the minutes and one fo setting the hours
There are also two buttons. A Play/Cancel button and a Pause/Resume button
Thanks in advance!
import UIKit
import AVFoundation
class Practice_Timmer_VC: UIViewController
{
#IBOutlet weak var navBar: UINavigationItem!
#IBOutlet weak var viewLabel: DesignableLabel!
#IBOutlet weak var timerLabel: DesignableLabel!
#IBOutlet weak var theTabbar: UITabBar!
#IBOutlet weak var minutesLabel: UILabel!
#IBOutlet weak var hoursLabel: UILabel!
var seconds: Int = 60
var timer = Timer()
var isTimerRunning: Bool = false
var resumeTapped: Bool = false
var theTime: String = ""
var startTime: Date = Date()
var endTime: Date = Date()
var total: Int = 0
var chimeSoundEffect: AVAudioPlayer?
override func viewDidLoad()
{
super.viewDidLoad()
theTabbar.selectedItem = theTabbar.items![4]
view.backgroundColor = UIColor(patternImage: UIImage(named: "Carbon.png")!)
startButton.isEnabled = true
pauseButton.isEnabled = false
populateTheTimer()
}// End of viewDidLoad
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
startButton.setImage(UIImage(named: "Play"), for: .normal)
}
func populateTheTimer()
{
let theName = ModelData.getTheTrickName()
navBar.title = theName
}
func getTheDifference(start: Date, end: Date)
{
let theFormatter = DateComponentsFormatter()
theFormatter.allowedUnits = [.hour, .minute]
theFormatter.unitsStyle = .full
theTime = theFormatter.string(from: start, to: end) ?? ""
}
func formattedDate() -> String
{
let formatter = DateFormatter()
let date = Date()
formatter.locale = Locale.current
formatter.dateStyle = .medium
return formatter.string(from: date)
}
func timeString(time: TimeInterval) -> String
{
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format:"%02i:%02i:%02i", hours, minutes, seconds)
}
func runTimer()
{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(self.updateTimer)), userInfo: nil, repeats: true)
isTimerRunning = true
pauseButton.isEnabled = true
}
#objc func updateTimer()
{
if seconds < 1
{
timer.invalidate()
endTime = Date()
getTheDifference(start: startTime, end: endTime)
startButton.setImage(UIImage(named: "Play"), for: .normal)
startButton.isEnabled = true
pauseButton.isEnabled = false
playTheSound()
showAlert()
} else {
seconds -= 1
timerLabel.text = timeString(time: TimeInterval(seconds))
}
}
func showAlert()
{
let theAlert = UIAlertController(title: "Practice Ended", message: "\(formattedDate())\nPracticed for: \(theTime)", preferredStyle: .alert)
let saveTheInfo = UIAlertAction(title: "Save Practice", style: .default) { (saveAction) in
self.gotoAddEdit()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (cancelAction) in
}
theAlert.addAction(saveTheInfo)
theAlert.addAction(cancel)
present(theAlert, animated: true)
}
func playTheSound()
{
let path = Bundle.main.path(forResource: "chime.mp3", ofType: nil)!
let theURL = URL(fileURLWithPath: path)
do {
chimeSoundEffect = try AVAudioPlayer(contentsOf: theURL)
chimeSoundEffect?.play()
} catch {
}
}
func gotoAddEdit()
{
let sb = UIStoryboard(name: "Main", bundle: nil)
if let addEdit = sb.instantiateViewController(withIdentifier: "Practice_Log_VC") as? Practice_Log_VC
{
addEdit.passData = "\(formattedDate())\nPracticed for: \(theTime)"
addEdit.delegate = self as? Timer2PracticeLog_Delegate
self.navigationController?.pushViewController(addEdit, animated: ModelData.isAnimation())
self.navigationController?.view.semanticContentAttribute = .forceLeftToRight
}
}
#IBOutlet weak var startButton: DesignableButton!
#IBAction func startButtonTapped(_ sender: DesignableButton)
{
let minutes2Seconds = Int(minutesSliderOutlet.value) * 60
let hours2Seconds = Int(hoursSliderOutlet.value) * 3600
seconds = Int(minutes2Seconds + hours2Seconds)
if isTimerRunning == false // Start
{
if seconds > 0
{
startTime = Date()
sender.setImage(UIImage(named: "Cancel_Video"), for: .normal)
runTimer()
} else {
view.sendConfirmationAlert(theTitle: "Error! Practice time is set to 0.", theMessage: "Please set the practice time.", buttonTitle: "OK")
minutesSliderOutlet.value = 1
minutesLabel.text = "1 Minute"
}
} else { // Cancel
endTime = Date()
timer.invalidate()
seconds = 0
getTheDifference(start: startTime, end: endTime)
sender.setImage(UIImage(named: "Play"), for: .normal)
minutesSliderOutlet.value = 1
minutesLabel.text = "1 Minute"
hoursSliderOutlet.value = 0
hoursLabel.text = "0 Hours"
timerLabel.text = timeString(time: TimeInterval(seconds))
isTimerRunning = false
pauseButton.isEnabled = false
showAlert()
}
}
#IBOutlet weak var pauseButton: DesignableButton!
#IBAction func pauseButtonTapped(_ sender: DesignableButton)
{
if resumeTapped == false
{
timer.invalidate()
resumeTapped = true
sender.setImage(UIImage(named: "Resume"), for: .normal)
} else {
runTimer()
resumeTapped = false
sender.setImage(UIImage(named: "Pause"), for: .normal)
}
}
#IBOutlet weak var minutesSliderOutlet: UISlider!
#IBAction func minuteSlider(_ sender: UISlider)
{
let minutes = Int(sender.value)
if minutes > 1
{
minutesLabel.text = String(minutes) + " Minutes"
} else {
minutesLabel.text = String(minutes) + " Minute"
}
}
#IBOutlet weak var hoursSliderOutlet: UISlider!
#IBAction func hoursSlider(_ sender: UISlider)
{
let hours = Int(sender.value)
if hours > 1
{
hoursLabel.text = String(hours) + " Hours"
} else {
hoursLabel.text = String(hours) + " Hour"
}
}
}// End of Class

Get the time when the user pauses the app as a new date(), and also the time when the user resumes the app. Get the difference between those two dates/times and subtract that from the total time to get the correct total time for the practice session.
For example, you could have a variable that keeps track of total pause time:
var totalPauseTime: TimeInterval = 0
var pauseStartTime: Date?
It gets recalculated every time a user taps the pause button to resume (after previously having tapped it to pause):
#IBAction func pauseButtonTapped(_ sender: DesignableButton)
{
if resumeTapped == false
{
pauseStartTime = Date()
timer.invalidate()
resumeTapped = true
sender.setImage(UIImage(named: "Resume"), for: .normal)
} else {
if let pauseStartTime = pauseStartTime {
totalPauseTime += Date().timeIntervalSinceReferenceDate - pauseStartTime.timeIntervalSinceReferenceDate
}
runTimer()
resumeTapped = false
sender.setImage(UIImage(named: "Pause"), for: .normal)
}
}
Haven't actually tested the above code but it should give an idea. Then you subtract totalPauseTime at the point in your code where you calculate the total session time.

Related

MPRemoteCommandCenter error: the track is played simultaneously several times

I have TableViewController and AudioPlayerViewController. I have a problem with using MPRemoteCommandCenter. For example: In TableViewController I click on cell and go toAudioPlayerViewController next I lock device and control my music with MPRemoteCommandCenter - all works fine. But if I further unlock device return to TableViewController go again to AudioPlayerViewController lock device and press play/pause button my music will play two times at the same time. If I will repeat the action my music will play three times at the same time. And etc... How to fix it?
code:
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController, AVAudioPlayerDelegate, UIAlertViewDelegate {
var audioPlayer: AVAudioPlayer!
let musicOperation = OperationQueue()
var timer: Timer?
#IBOutlet weak var playButton: UIButton!
#IBOutlet var timeElapsed: UILabel!
#IBOutlet var timeDuration: UILabel!
#IBOutlet weak var logo: UIImageView!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var volumeView: UIView!
var index = 0
var buttonIndex = 0
var endOfChapterSleepTimer = false
override func viewDidLoad() {
super.viewDidLoad()
// Table View Index
buttonIndex = masterIndex
musicOperation.maxConcurrentOperationCount = 1
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
UserDefaults.standard.set(index, forKey: "index")
if index > 0 {
let fileManager = FileManager.default
let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
if let documentDirectoryURL: NSURL = urls.first as NSURL? {
let soundURL = documentDirectoryURL.appendingPathComponent("/\(masterIndex)/\(index).mp3")
UserDefaults.standard.set(index, forKey: "\(masterIndex)")
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL!)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
restorePlayerCurrentTime()
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
} else {
let url = Bundle.main.url(forResource: "\(masterIndex)0", withExtension: "m4a")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
play(sender:AnyObject.self as AnyObject)
setupMediaPlayerNotificationView()
lockScreen()
} catch {
}
}
}
// MARK: - Audio player controller
#IBAction func play(sender: AnyObject) {
if !audioPlayer.isPlaying{
audioPlayer.play()
slider.maximumValue = Float(audioPlayer.duration)
timer = Timer(timeInterval: 0.1, target: self, selector: #selector(self.updateTime), userInfo: nil, repeats: true)
RunLoop.main.add(timer!, forMode: .commonModes)
restorePlayerCurrentTime()
playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
} else {
audioPlayer.pause()
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
timer?.invalidate()
}
}
#IBAction func fastForward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time += 15.0 // Go Forward by 15 Seconds
if time > audioPlayer.duration {
audioPlayerDidFinishPlaying(audioPlayer, successfully: true)
} else {
audioPlayer.currentTime = time
updateTime()
}
}
#IBAction func fastBackward(sender: AnyObject) {
var time: TimeInterval = audioPlayer.currentTime
time -= 15.0 // Go Back by 15 Seconds
if time < 0 {
audioPlayer.currentTime = 0
updateTime()
} else {
audioPlayer.currentTime = time
updateTime()
}
}
// MARK: - Audio player time
private func restorePlayerCurrentTime() {
let currentTimeFromUserDefaults : Double? = UserDefaults.standard.value(forKey: "currentTime\(masterIndex)\(index)") as! Double?
if let currentTimeFromUserDefaultsValue = currentTimeFromUserDefaults {
audioPlayer.currentTime = currentTimeFromUserDefaultsValue
slider.value = Float.init(audioPlayer.currentTime)
}
}
#objc func updateTime() {
let currentTime = Int(audioPlayer.currentTime)
let minutes = currentTime/60
let seconds = currentTime - minutes * 60
let durationTime = Int(audioPlayer.duration) - Int(audioPlayer.currentTime)
let minutes1 = durationTime/60
let seconds1 = durationTime - minutes1 * 60
timeElapsed.text = NSString(format: "%02d:%02d", minutes,seconds) as String
timeDuration.text = NSString(format: "-%02d:%02d", minutes1,seconds1) as String
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
}
func audioPlayerDidFinishPlaying(_ audioPlayer: AVAudioPlayer, successfully flag: Bool) {
playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
let currentTime = 0
let durationTime = 0.1
UserDefaults.standard.set(currentTime, forKey: "currentTime\(masterIndex)\(index)")
UserDefaults.standard.set(durationTime, forKey: "durationTime\(masterIndex)\(index)")
slider.value = Float.init(audioPlayer.currentTime)
timer?.invalidate()
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let documentDirectoryPath:String = path[0]
let fileManager = FileManager()
let destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/\(masterIndex)/\(index+1).mp3"))
if fileManager.fileExists(atPath: destinationURLForFile.path){
index = index + 1
viewDidLoad()
} else {
}
}
// MARK: - Audio player lock screen
func lockScreen() {
var albumArtwork : MPMediaItemArtwork!
let image:UIImage = UIImage(named: "infoImage")!
albumArtwork = MPMediaItemArtwork.init(boundsSize: image.size, requestHandler: { (size) -> UIImage in
return image
})
let infotitle = "title"
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyArtist : "",
MPMediaItemPropertyTitle : infotitle,
MPMediaItemPropertyArtwork : albumArtwork,
MPMediaItemPropertyAlbumTitle : "",
MPNowPlayingInfoPropertyElapsedPlaybackTime : Int(audioPlayer.currentTime),
MPMediaItemPropertyPlaybackDuration: Int(audioPlayer.duration)]
}
func setupMediaPlayerNotificationView() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.addTarget { event in
self.audioPlayer.play()
self.lockScreen()
self.playButton.setImage(UIImage(named: "pause.png"), for: UIControlState.normal)
print("play")
return .success
}
commandCenter.pauseCommand.addTarget { event in
self.audioPlayer.pause()
self.lockScreen()
self.playButton.setImage(UIImage(named: "play.png"), for: UIControlState.normal)
print("pause")
return .success
}
commandCenter.skipBackwardCommand.preferredIntervals = [15]
commandCenter.skipBackwardCommand.addTarget { event in
self.fastBackward(sender: self)
self.lockScreen()
print("fastBackward")
return .success
}
commandCenter.skipForwardCommand.preferredIntervals = [15]
commandCenter.skipForwardCommand.addTarget { event in
self.fastForward(sender: self)
self.lockScreen()
print("fastForward")
return .success
}
commandCenter.changePlaybackPositionCommand.addTarget(self, action: #selector(self.changedThumbSlider(_:)))
#objc func changedThumbSlider(_ event: MPChangePlaybackPositionCommandEvent) -> MPRemoteCommandHandlerStatus {
let time = event.positionTime
audioPlayer.currentTime = TimeInterval(time)
self.lockScreen()
return .success
}
// MARK: - Audio player slider
#IBAction func slide(_ slider: UISlider) {
musicOperation.cancelAllOperations()
let operation = BlockOperation()
audioPlayer.currentTime = TimeInterval(slider.value)
self.lockScreen()
musicOperation.addOperation(operation)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
audioPlayer.pause()
timer?.invalidate()
musicOperation.cancelAllOperations()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.setAnimationsEnabled(true)
// Navigation Bar
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 55/255, green: 60/255, blue: 65/255, alpha: 1.0)
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.navigationBar.tintColor = .white
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
whats happening is that everytime you goto AudioPlayerViewController you enable MPRemoteCommandCenter, however when you go back to TableViewController you are not calling removeTarget. this is your issue.
calling something like the below in viewWillDisappear to removeTarget
func setupMediaPlayerNotificationView(_ enable: Bool) {
let commandCenter = MPRemoteCommandCenter.shared()
if enable {
commandCenter.playCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.addTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.addTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.addTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
} else {
commandCenter.playCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.pauseCommand.removeTarget(self, action: #selector(self.lockScreenPlay(_:)))
commandCenter.nextTrackCommand.removeTarget(self, action: #selector(self.lockScreenNextTrack(_:)))
commandCenter.previousTrackCommand.removeTarget(self, action: #selector(self.lockScreenPreviousTrack(_:)))
}
}
example to follow up question:
#objc func lockScreenPlay(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playButton(AnyObject.self)
return .success
}
#objc func lockScreenNextTrack(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
self.playerDidFinishPlaying()
return .success
}

Randomly display an element from a file in bundle every time a button is pressed

So I have this big file with more than 600 words. I add them in my array and now I need to randomly show them on the Label. Every time a button is pressed the new random word has to be shown until the timer is ended. I've searched multiple sources for this question but couldn't apply them, because I'm new to this
import UIKit
class ViewController: UIViewController {
var wordArray: [String] = []
var i: Int = 0
var timer = Timer()
var totalSecond = 5
func startTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#objc func updateTime() {
if totalSecond != 0 {
totalSecond -= 1
timerLabel.text = "\(totalSecond) seconds left"
} else {
endTimer()
}
}
func endTimer() {
timer.invalidate()
}
func timeFormatted(_ totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
return String(format: "0:%02d", seconds)
}
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var showWordLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = Bundle.main.url(forResource: "english_words", withExtension: "txt")!
do {
let string = try String(contentsOf: url, encoding: .utf8)
wordArray = string.components(separatedBy: CharacterSet.newlines)
} catch {
print(error)
}
startTimer()
}
#IBAction func nextBtn(_ sender: UIButton) {
}
}
If you use swift 4.2 and above you can use randomElement() It returns an optional to avoid the empty case.
let yourArray = ["one", "two", "three", "Four"]
if let randomName = yourArray.randomElement() {
print(randomName)
}
if you use Swift 4.1 or below, you do in this way:
let array = ["one", "two", "three", "Four"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])
Although I'm not 100% sure I understand you problem here a modified version of your viewController.
See comments in code.
class ViewController: UIViewController {
var wordArray: [String] = []
var i: Int = 0
var timer = Timer()
var totalSecond = 5
func startTimer() {
// Stop old timer
timer.invalidate()
// Start new timer
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
#objc func updateTime() {
if totalSecond != 0 {
totalSecond -= 1
timerLabel.text = "\(totalSecond) seconds left"
} else {
endTimer()
}
}
func endTimer() {
timer.invalidate()
}
func timeFormatted(_ totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
return String(format: "0:%02d", seconds)
}
#IBOutlet weak var timerLabel: UILabel!
#IBOutlet weak var showWordLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = Bundle.main.url(forResource: "english_words", withExtension: "txt")!
do {
let string = try String(contentsOf: url, encoding: .utf8)
wordArray = string.components(separatedBy: CharacterSet.newlines)
} catch {
print(error)
}
startTimer()
}
#IBAction func nextBtn(_ sender: UIButton) {
// Fetch random Word from array
let randomValue = Int.random(in: 0..<wordArray.count)
let randomWord = wordArray[randomValue]
// Assign to label
showWordLabel.text = randomWord
// Restart timer
startTimer()
}
}

How to save a users progress in a Quiz game

I am wondering how I should save the users progress in my quiz game. Every time the user closes the app it resets. Here's my code:
import UIKit
import GoogleMobileAds
class ViewController: UIViewController, GADBannerViewDelegate {
//Place your instance variables here
let allQuestions = QuestionBank()
var pickedAnswer : Bool = false
var questionNumber : Int = 0
var score : Int = 0
let googleAdTestID: String = "ca-app-pub-3940256099942544/2934735716"
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var bannerView: GADBannerView!
#IBOutlet weak var progressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion() )
bannerView.adUnitID = googleAdTestID
bannerView.rootViewController = self
bannerView.load(GADRequest())
nextQuestion()
}
#IBAction func skipButton(_ sender: Any) {
skip()
}
func skip() {
if score <= 4 {
}
else if score >= 5 {
questionNumber = questionNumber + 1
nextQuestion()
score = score - 5
scoreLabel.text = "Coins: \(score)"
}
}
#IBAction func answerPressed(_ sender: AnyObject) {
if sender.tag == 1 {
pickedAnswer = true
}
else if sender.tag == 2 {
pickedAnswer = false
}
checkAnswer()
questionNumber = questionNumber + 1
nextQuestion()
}
func updateUI() {
scoreLabel.text = "Coins: \(score)"
progressLabel.text = "Question Number: \(questionNumber + 1)"
}
func nextQuestion() {
if questionNumber <= 37 {
questionLabel.text = allQuestions.list[questionNumber].questionText
updateUI()
}
else {
let alert = UIAlertController(title: "Awesome", message: "You have finished all the questions!! Do you want to start over?", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Restart", style: .default, handler: { (UIAlertAction) in
self.startOver()
})
alert.addAction(restartAction)
present(alert, animated: true, completion: nil)
}
}
func checkAnswer() {
let correctAnswer = allQuestions.list[questionNumber].answer
if correctAnswer == pickedAnswer {
print("Correct!")
ProgressHUD.showSuccess("Correct!!")
// varible += 1 same as varible = varible + 1
score += 1
}
else {
print("wrong!")
ProgressHUD.showError("Wrong!!")
}
}
func startOver() {
questionNumber = 0
nextQuestion()
score = 0
scoreLabel.text = "Score: 0"
}
}
Save
// save necessory information using UserDefaults
// Add this code at your business logic
let database = UserDefaults.standard
let questionNumber = 2
database.set(questionNumber, forKey: "LAST_ANSWERED_QUESTION_NUMBER")
let score = 23
database.set(score, forKey: "QUIZ_SCORE")
let sync = database.synchronize()
if sync{
print("userdefaults - sync done")
}else{
print("userdefaults - failed to sync")
}
Fetch
//access stored values from UserDefaults
// Add this code at your business logic
let database = UserDefaults.standard
if let lastAnswredQuestion = database.value(forKey: "LAST_ANSWERED_QUESTION_NUMBER") as? Int{
print("LAST_ANSWERED_QUESTION_NUMBER \(lastAnswredQuestion)")
}
if let scored = database.value(forKey: "QUIZ_SCORE") as? Int{
print("QUIZ_SCORE \(scored)")
}
NOTE: The data will be stored until the app is installed, if the user deletes the app then whole data will vanish from UserDefaults.

Retain countdown timer value when switching between view controllers

I'm working on a trip countdown app that also has weather forecast built in.
Things are working pretty well, but when I switch from countdown timer to weather forecast, then come back, the countdown timer is reset.
Here's my countdown view controller code:
import UIKit
class CountdownVC: UIViewController {
var timer = Timer()
let userCalendar = Calendar.current
let requestedComponent: Set<Calendar.Component> = [.day,.hour,.minute,.second]
var departureDateTime: Date?
#IBOutlet weak var imageView: UIImageView!
let images = [
UIImage(named: "MK.png")!,
UIImage(named: "SSE1.png")!,
UIImage(named: "TofT.png"),
UIImage(named: "TofL1.png")!]
var index = 0
let animationDuration: TimeInterval = 0.25
let switchingInterval: TimeInterval = 15
func animateImageView()
{
CATransaction.begin()
CATransaction.setAnimationDuration(animationDuration)
CATransaction.setCompletionBlock {
DispatchQueue.main.asyncAfter(deadline: .now() + self.switchingInterval) {
self.animateImageView()
}
}
let transition = CATransition()
transition.type = kCATransitionFade
imageView.layer.add(transition, forKey: kCATransition)
imageView.image = images[index]
imageView.contentMode = .scaleAspectFill
imageView.alpha = 0.5
CATransaction.commit()
index = index < images.count - 1 ? index + 1 : 0
}
#IBOutlet weak var departureDateTimePicker: UIDatePicker!
#IBAction func departureDateTimePicker(_ sender: Any) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yy H:mm:ss"
departureDateTimePicker.addTarget(self, action: #selector(handler), for: UIControlEvents.valueChanged)
}
#IBOutlet weak var selectDepartureDateTimeLabel: UILabel!
#IBOutlet weak var daysLabel: UILabel!
#IBOutlet weak var hoursLabel: UILabel!
#IBOutlet weak var minutesLabel: UILabel!
#IBOutlet weak var secondsLabel: UILabel!
func handler(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yy H:mm:ss"
let departureDateTimeString = dateFormatter.string(from: departureDateTimePicker.date)
departureDateTime = dateFormatter.date(from: departureDateTimeString)
}
func printTime() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yy H:mm:ss"
let startTime = Date()
let endTime = departureDateTime
let timeDifference = userCalendar.dateComponents(requestedComponent, from: startTime, to: endTime!)
let startTimeDouble: Double = startTime.timeIntervalSinceReferenceDate
var endTimeDouble: Double?
endTimeDouble = (endTime?.timeIntervalSinceReferenceDate)
if endTimeDouble! > startTimeDouble {
daysLabel.text = "\(timeDifference.day!) Days"
hoursLabel.text = "\(timeDifference.hour!) Hours"
minutesLabel.text = "\(timeDifference.minute!) Minutes"
secondsLabel.text = "\(timeDifference.second!) Seconds"
} else {
timer.invalidate()
daysLabel.text = ""
hoursLabel.text = ""
minutesLabel.text = ""
secondsLabel.text = ""
}
}
func runTimer() {
let timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(printTime), userInfo: nil, repeats: true)
timer.fire()
}
#IBOutlet weak var startBtn: UIButton!
#IBAction func startBtnPressed(_ sender: Any) {
handler(sender: departureDateTimePicker)
printTime()
runTimer()
selectDepartureDateTimeLabel.text = "Time to departure"
departureDateTimePicker.isHidden = true
startBtn.isHidden = true
}
#IBOutlet weak var resetBtn: UIButton!
#IBAction func resetBtnPressed(_ sender: Any) {
timer.invalidate()
selectDepartureDateTimeLabel.text = "Select departure date & time"
departureDateTimePicker.isHidden = false
startBtn.isHidden = false
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/dd/yy H:mm:ss"
let startTime = Date()
let endTime = startTime
let timeDifference = userCalendar.dateComponents(requestedComponent, from: startTime, to: endTime)
daysLabel.text = "\(timeDifference.day!) Days"
hoursLabel.text = "\(timeDifference.hour!) Hours"
minutesLabel.text = "\(timeDifference.minute!) Minutes"
secondsLabel.text = "\(timeDifference.second!) Seconds"
}
#IBAction func goToWeatherBtnPressed(_ sender: Any) {
performSegue(withIdentifier: "goToWeatherVC", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
imageView.image = images[index+1]
animateImageView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Changing Label Text & Colour Depending On Current Time

I am trying to change the label that you see in my screenshot that has a green background and says We Are Open.
I would like the bottom label to turn RED and say "Sorry we are closed" whenever the listed opening times have passed and then go back to GREEN and say "We Are Open" at the correct opening times.
I've managed to import date and time successfully into the top label but I'm not sure how do the bottom label.
Here is the code:
import UIKit
import Firebase
import FirebaseInstanceID
import FirebaseMessaging
class FirstViewController: UIViewController {
var timer = Timer()
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var openStatusLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
FIRMessaging.messaging().subscribe(toTopic: "/topics/news")
self.timer = Timer.scheduledTimer(timeInterval: 1.0,
target: self,
selector: #selector(FirstViewController.tick),
userInfo: nil,
repeats: true)
}
#objc func tick() {
timeLabel.text = DateFormatter.localizedString(from: NSDate() as Date,
dateStyle: .medium,
timeStyle: .medium)
}
}
Below code i have try and it will work fine. Initially i created two UILabel with proper constraints. Then Create outlets for two label to view controller. Then try this code.
import UIKit
extension NSDate {
func dayOfWeek() -> Int? {
guard
let calender: NSCalendar = NSCalendar.currentCalendar(),
let component: NSDateComponents = calender.components(.Weekday, fromDate: self) else { return nil }
return component.weekday
}
}
class ViewController: UIViewController {
#IBOutlet var timeOfTheDay: UILabel! //Top Label for showing current time
#IBOutlet var Status: UILabel! //Status Label for showing open or close
override func viewDidLoad() {
super.viewDidLoad()
self.dateCheck()
}
func dateCheck()
{
let today = NSDate().dayOfWeek()
if today == 1
{
//print("Sunday")
self.closed()
}
else if today == 2
{
//print("Monday")
self.closed()
}
else if today == 3
{
//print("Tuesday")
self.uptoEvening()
}
else if today == 4
{
//print("Wednesday")
self.uptoNight()
}
else if today == 5
{
// print("Thursday")
self.uptoNight()
}
else if today == 6
{
//print("Friday")
self.uptoNight()
}
else
{
//print("Saturday")
self.uptoEvening()
}
}
func getTime() -> (hour:Int, minute:Int, second:Int) {
let currentDateTime = NSDate()
let calendar = NSCalendar.currentCalendar()
let component = calendar.components([.Hour,.Minute,.Second], fromDate: currentDateTime)
let hour = component.hour
let minute = component.minute
let second = component.second
return (hour,minute,second)
}
func closed()
{
timeOfTheDay.text = String(getTime().hour)+" : "+String(getTime().minute)+" : "+String(getTime().second)
timeOfTheDay.backgroundColor = UIColor.redColor()
timeOfTheDay.textColor = UIColor.whiteColor()
Status.text = "Sorry! Today, We are Closed!"
Status.backgroundColor = UIColor.redColor()
Status.textColor = UIColor.whiteColor()
}
func opened(endTime:String)
{
timeOfTheDay.text = String(getTime().hour)+" : "+String(getTime().minute)+" : "+String(getTime().second)
timeOfTheDay.backgroundColor = UIColor.greenColor()
timeOfTheDay.textColor = UIColor.whiteColor()
Status.text = "Hi! still we are opened upto "+endTime
Status.backgroundColor = UIColor.greenColor()
Status.textColor = UIColor.whiteColor()
}
func uptoEvening()
{
let time = getTime().hour
switch time
{
case 09...16: opened("17") //set time for 09:00 to 16:59
default:closed()
}
}
func uptoNight()
{
let time = getTime().hour
switch time
{
case 09...20: opened("21") //set time for 09:00 to 20:59
default:closed()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Extension for swift 3:
extension Date {
func dayOfWeek() -> Int? {
let calender: Calendar = Calendar.current
let component: DateComponents = (calender as NSCalendar).components(.weekday, from: self)
return component.weekday
}
}

Resources