Decrease the value of the variable containing the current time with UISlider - ios

I don't know how to make the current time value (returned by the dateFormatter variable) change when scrolling the slider.
I tried to convert the dateFormatter variable to double but without success.
var c: Double
c = Double(dateFormatter.string(from: Date())
I would like to decrease the value of the time -1 hour for each shift of the slider thumb.
The changeTime function has to take care of changing the time value when moving the slider.
#IBOutlet weak var sliderTime: UISlider!
#IBOutlet var labelTime: UILabel!
var timer = Timer()
var dateFormatter = DateFormatter()
override func viewDidLoad()
{
dateFormatter.timeStyle = .short
labelTime.text = dateFormatter.string(from: Date())
timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(updateTimeLabel), userInfo: nil, repeats: true);
}
#objc func updateTimeLabel() {
labelTime.text = dateFormatter.string(from: Date())
}
#IBAction func changeTime(_ sender: Any) {
}

Swift 5
Whenever you update the date, decrease the number of hours dedicated by slider from the current date, try this:
class ViewController: UIViewController
{
#IBOutlet weak var simpleSlider: UISlider!
#IBOutlet weak var dateLabel: UILabel!
var timer = Timer()
var currentSliderStep: Int = 0
var dateFormatter = DateFormatter()
override func viewDidLoad()
{
dateFormatter.timeStyle = .short
labelTime.text = dateFormatter.string(from: Date())
timer = Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(updateTimeLabel), userInfo: nil, repeats: true);
}
#objc func updateTimeLabel()
{
let newDate = Calendar.current.date(byAdding: .hour, value: -currentSliderStep, to: Date())!
labelTime.text = dateFormatter.string(from: newDate)
}
#IBAction func sliderMoved(sender: AnyObject)
{
let value = simpleSlider.value
let step: Float = 1
let roundedValue = round(value / step) * step
currentSliderStep = Int(roundedValue)
}
}
Note: With the Slider, the "Primary Action Triggered" event is triggered when it is adjusted. This is the perfect event to handle (link this event to sliderMoved).

Related

How to add mutiple progress views to a timer?

here is all of the code that I am using for this project. I have four progress views and when the first end an alarm goes off and then the next start eminently and then goes to the next and then to the fourth one where the timer ends after that and the timer goes off one more time and then the timers stop.
import UIKit
import AVFoundation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnPressed4(_ sender: Any) {
let currentDateTime = Date()
let formatter = DateFormatter()
formatter.timeStyle = .short
let dateTimeString = formatter.string(from: currentDateTime)
timePrint4.text = dateTimeString
btnPressed4.titleLabel?.textColor = UIColor.white
}
#IBAction func btnPressed3(_ sender: Any) {
let currentDateTime = Date()
let formatter = DateFormatter()
formatter.timeStyle = .short
let dateTimeString = formatter.string(from: currentDateTime)
timePrint3.text = dateTimeString
btnPressed3.titleLabel?.textColor = UIColor.white
}
#IBAction func btnPressed2(_ sender: UIButton) {
let currentDateTime = Date()
let formatter = DateFormatter()
formatter.timeStyle = .short
let dateTimeString = formatter.string(from: currentDateTime)
timePrint2.text = dateTimeString
btnPressed2.titleLabel?.textColor = UIColor.green
}
#IBAction func btnPressed1(_ sender: UIButton) {
let currentDateTime = Date()
let formatter = DateFormatter()
formatter.timeStyle = .short
let dateTimeString = formatter.string(from: currentDateTime)
timePrint1.text = dateTimeString
}
#IBOutlet weak var btnPressed1: UIButton!
#IBOutlet weak var btnPressed2: UIButton!
#IBOutlet weak var btnPressed3: UIButton!
#IBOutlet weak var btnPressed4: UIButton!
#IBOutlet weak var timePrint1: UILabel!
#IBOutlet weak var timePrint2: UILabel!
#IBOutlet weak var timePrint4: UILabel!
#IBOutlet weak var timePrint3: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var progressBar1: UIProgressView!
#IBOutlet weak var progressBar2: UIProgressView!
#IBOutlet weak var progressBar3: UIProgressView!
#IBOutlet weak var progressBar4: UIProgressView!
let start = 5
var timer = Timer()
var player: AVAudioPlayer!
var totalTime = 0
var secondsPassed = 0
#IBAction func startButtonPressed(_ sender: UIButton) {
let startB = sender.titleLabel?.text
totalTime = start
progressBar1.progress = 0.0
secondsPassed = 0
titleLabel.text = "coffee timer"
btnPressed1.titleLabel?.textColor = UIColor.white
timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(updateTimer), userInfo:nil, repeats: true)
}
#objc func updateTimer() {
if secondsPassed < totalTime {
secondsPassed += 1
progressBar1.progress = Float(secondsPassed) / Float(totalTime)
print(Float(secondsPassed) / Float(totalTime))
} else {
timer.invalidate()
titleLabel.text = "check coffee"
btnPressed1.titleLabel?.textColor = UIColor.green
let url = Bundle.main.url(forResource: "alarm_sound", withExtension: "mp3")
player = try! AVAudioPlayer(contentsOf: url!)
player.play()
}
}
}
There's lots of different ways to handle this. Here is an outline of a possible approach:
Have an instance variable currentProgress, of type Int? If it's nil, none of your progress indicators is running.
Have an instance var stepCompletedInterval: Double
Create a struct to hold the different views you use to manage a progress step:
struct ProgressInfo {
let aButton: UIButton
let aLabel: UILabel
let aProgressIndicator: UIProgressIndicator
let secondsForThisStep: Int
}
Create an array of ProgressInfo, (let's call it progressInfoArray) and populate it with the buttons, labels, and progress indicators you have above. Also give each entry a value for secondsForThisStep.
When the user starts the process, set currentProgress to 0 (The first progress indicator.)
Now, for each step, set a stepCompletedInterval to the current time plus progressInfoArray[currentProgress].secondsForThisStep (The time at which the current step should complete.
Also start a 1-second repeating timer. Each time the timer fires, check to see if the current time is greater than stepCompletedInterval. If it is, increment currentProgress if there are still more steps to complete, and repeat the "now for each step" part above.
That is a rough outline of how you might go about it. I'm not completely clear on what your start buttons and btnPressed1 to btnPressed4 are supposed to do, so I'll leave that for you.
You'll need to adapt the approach I've outlined to your needs. It isn't code, it's an approach. I'm not going to give you code.

How to capture the elapsed time when a timer is paused

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.

Passing data from model to controller

I am wondering what would be the best practice when I want to pass data from model to controller.
What I want to do
I would like to update a label when the time changes.
CurrentTime.swift (Model)
var timer: Timer?
var currentTime: String?
init() {
if timer == nil{
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateCurrentTime), userInfo: nil, repeats: true)
}
}
#objc private func updateCurrentTime(){
let df = DateFormatter()
df.dateFormat = "HH:mm"
df.timeZone = TimeZone.current
let timezoneDate = df.string(from: Date())
currentTime = timezoneDate
}
ViewController.swift
class ViewController: UIViewController {
#IBOutlet var timeLabel: UILabel!
var currentTime = CurrentTime()
override func viewDidLoad() {
}
#IBAction func closeBtnWasPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
Option 1
1- Add this var
weak var delegate: ViewController?
2- in viewDidLoad of the vc
currentTime.delegate = self
3-
let timezoneDate = df.string(from: Date())
currentTime = timezoneDate
delegate?.update(currentTime)
4- inside the vc
func update(_ data:String) {
lbl.text = data
}
off course you can do
delegate?.lbl.text = currentTime
but above is MVC
Option 2
var ob:NSKeyValueObservation!
and in viewDidLoad
ob = currentTime.observe(\CurrentTime.currentTime, options: .new) { cur, tex in
timeLabel.text = tex
}
You should define a Dynamic class like this:
class Dynamic<T> {
var bind: (T) -> Void = { _ in }
var value: T? {
didSet {
bind(value!)
}
}
init(_ v: T) {
value = v
}
}
In your model change definition of the currentTime property like this:
var currentTime: Dynamic<String>
In your controller in viewDidLoad method add these codes:
yourModel.currentTime.bind = { [weak self] time in
self?.timeLabel.text = time
}

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.
}
}

Change Date Using Stepper - Swift

I am trying to figure out a way to allow a user to change current date represented as a label.
The change/action button is a Stepper
The label is defaulting to the current date and when they click the stepper they should be able to change the date to tomorrow, next day, and so on...when they click the + on the stepper. Then the opposite for clicking - on the stepper.
class DateTestViewController: UIViewController {
#IBOutlet weak var stepperOutlet: UIStepper!
#IBOutlet weak var dateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let now = NSDate()
var daysToAdd: Double = 1
var newDate = now.dateByAddingTimeInterval(60*60*24*daysToAdd)
dateLabel.text = "\(newDate)"
}
#IBAction func stepperAction(sender: AnyObject) {
var unitsValue =
dateLabel.text = "\(unitsValue)"
}
}
Can someone please point me in the right direction to solve this issue.
Thanks,
Addison
update: Xcode 7.3 • Swift 2.2
You can use NSCalendar method dateByAddingUnit to add or subtract the value from your UIStepper from your date as follow:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var strDate: UILabel!
var selectedDate:NSDate!
override func viewDidLoad() {
super.viewDidLoad()
selectedDate = NSDate() // sets the initial date
strDate.text = selectedDate.formatted
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func stepAction(sender: UIStepper) {
selectedDate = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!.dateByAddingUnit(.Day, value: Int(sender.value), toDate: NSDate(), options: [])!
strDate.text = selectedDate.formatted
}
}
extension NSDate {
var formatted: String {
let df = NSDateFormatter()
df.dateFormat = "MMMM dd, yyyy - EEEE"
df.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return df.stringFromDate(self)
}
}

Resources