iOS Training from beginner to advanced
When you set an appointment in the calendar app there is a control used for time measurements called the
UIDat
ePicker
. While not one of the most popular controls, it is very handy for working with dates. In this lesson we’ll find out how easy it is to work with. We also will cover some of the important date-related methods and classes you should know when you works with dates and times.
As a demonstration, we’ll create a delivery time estimator. Create a new project named SwiftDatePickerDemo from the single view template. On the storyboard set up like this:
Open the assistant editor. From the UIDatePicker, Top label, and upper segmented control, add outlets to your view controller:
//MARK: Outlets and properties @IBOutlet weak var myDatePicker: UIDatePicker! @IBOutlet weak var deliveryTimeLabel: UILabel! @IBOutlet weak var deliveryDelayPreset: UISegmentedControl!
From the date picker and the both segmented controls, add actions.
//MARK: - Target Actions @IBAction func myDateView(sender: UIDatePicker) { } @IBAction func deliveryDelayPreset(sender: UISegmentedControl) { } @IBAction func deliveryDate(sender: AnyObject) { }
Close the assistant editor and go to the viewcontroller.swift
file. UIDatePicker
has a few properties you can set to control the behavior of the picker. In a delivery application for example, we don’t want to deliver into the past so the first date and time that shows up is the current date and time. The picker will default to the current date and time, but we can set it. We can also set a minimum time on the picker. We will set if the date will show on the picker. For a simple delivery app where we will deliver only the same day, we really don’t need the extra wheels for the date.
Change viewDidLoad
to the following:
//MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() myDatePicker.datePickerMode = UIDatePickerMode.Time // 4- use time only let currentDate = NSDate() //5 - get the current date myDatePicker.minimumDate = currentDate //6- set the current date/time as a minimum myDatePicker.date = currentDate //7 - defaults to current time but shows how to use it. }
Line 4 uses the datePickerMode
property to set the mode to .Time
We then get a new instance of a NSDate
, which is the current time. We set that time in the minimumDate
property and the date
property.
Build and Run. While it doesn’t do much yet, you will find you cannot go to a time before the current time.
When we set the date
property, we did so as a NSDate
Class. What we would like to do is display the time on the label, but for that we need a String. Apparently one of the best kept secrets (AKA poorly documeted) in Xcode is how to do this. There is a special class for this, NSDateFormatter
, which is not quite intuitive to use. To get a string of the date, you make a instance of NSDateFormatter
,then set the properties for the instance. You use the instance method stringFromDate
to return a string from a date. The reason for all this is localization. NSDateFormatter
uses the region settings to display the date properly for that user’s experience.
Above the life cycle MARK
, add the following:
//MARK: - Instance Methods func printDate(date:NSDate){ let dateFormatter = NSDateFormatter()//3 var theDateFormat = NSDateFormatterStyle.ShortStyle //5 let theTimeFormat = NSDateFormatterStyle.ShortStyle//6 dateFormatter.dateStyle = theDateFormat//8 dateFormatter.timeStyle = theTimeFormat//9 deliveryTimeLabel.text = "Delivered at: " + dateFormatter.stringFromDate(date)//11 }
Line 3 creates a date formatter. Both date and time are in a date, so we need to set how we want to show the date and time. our choices are NoStyle
, ShortStyle
, MediumStyle
and LongStyle
. In line 5 and 6 we set this for the time and date. In line 11, we use the stringFromDate
method to return a string and directly send it to the label.
There is a shorter way to do this from a class method of NSDateFormatter
, but I don’t find it very useful.
let deliveryTime = NSDateFormatter.localizedStringFromDate(date, dateStyle: .ShortStyle, timeStyle: .ShortStyle) deliveryTImeLabel.text = "Delivered at: " + deliveryTime
Generally if there is one date on something, there is more than one date or time. Consider this scene from Interval RunCalc, an App I wrote for calculating pace and distance given running intervals.
There are four times here: two in each table cell, one in the header and one in the footer. This is the common case, and having a date formatter around actually speeds up the process, since you set the style only once and then use the same stringfromDate
for all the occurrences.
Now that we can get a date into the label, we need to get the date. Change the myDateView
method to:
@IBAction func myDateView(sender: UIDatePicker) { printDate(sender.date) }
We already set the date
property. Here we get the date selected, and call the printDate
method to display it on the label.
Build and run, and now we can see the time in the label.
Of course no one delivers immediately, and the idea of this app is to calculate what time the delivery will be ready from that delay. We have a segmented control with the standard delay times in them. We will add those to the time and display the time plus the delay.
To do that we will need another property. Add this to the properties and outlets:
var delay:NSTimeInterval = 0.0
Add the following to the instance methods:
func delayTime() -> NSDate{ let pickerTime = myDatePicker.date return pickerTime.dateByAddingTimeInterval(delay)//3 }
Line three has NSDate
‘s method for taking an NSTimeInterval
and adding it to an NSDate
we get from the picker’s date
property. NSTimeIntervals
are really of type Double
, containing a time interval in number of seconds.
The times on the labels are not numbers, they are strings. We need to get them into type Double
to do anything with them.
@IBAction func deliveryDelayPreset(sender: UISegmentedControl) { let index = sender.selectedSegmentIndex //2 let delayString:NSString = sender.titleForSegmentAtIndex(index)! //3 delay = delayString.doubleValue * 60 //4 convert minutes to seconds printDate(delayTime()) //5 }
In lines 2 and 3 we get the title string back from the segment. In line 4 we use the doubleValue
method to convert the String
to a Double
, them multiply by 60 to get seconds. Line 5 prints the date again, this time with the delay.
We will need to change the mydateView
method as well so it reflects the delay:
@IBAction func myDateView(sender: UIDatePicker) { printDate(delayTime()) }
When we making deliveries today, we don’t need the date — it’s today’s date. Let’s use the other segmented control to set a boolean variable that will then change the formatting.
Add this to the outlets and properties:
var today = true
Change deliveryDate
to this:
@IBAction func deliveryDate(sender: UISegmentedControl) { let index = sender.selectedSegmentIndex today = index == 0 if today { myDatePicker.datePickerMode = .Time } else{ myDatePicker.datePickerMode = .DateAndTime } printDate(delayTime()) }
We set a flag today
and toggle the picker mode to show and hide the date. In the printDate
function change this:
var theDateFormat = NSDateFormatterStyle.NoStyle
to this:
var theDateFormat = NSDateFormatterStyle.NoStyle if !today { theDateFormat = .ShortStyle }
Build and run. Switch the segment from today to future.
The is still a subtle bug in the application. If you run the app for a long time, you can send pizzas back in time. Run the app, and wait a few minutes. You find you can dial back the date picker to the original initialization time. If you use an minimum time of the current time, you will need to update it frequently. I did it this way:
func delayTime() -> NSDate{ myDatePicker.minimumDate = NSDate() let pickerTime = myDatePicker.date return pickerTime.dateByAddingTimeInterval(delay) }
Since the date calculation occurs here, By setting the minimum date in the calculation, we prevent and catch all backwards dates, and reset them to the current date in one statement.
// // ViewController.swift // SwiftDatePickerDemo // // Created by Steven Lipton on 9/21/14. // Copyright (c) 2014 MakeAppPie.Com. All rights reserved. // import UIKit class ViewController: UIViewController { //MARK: Outlets and properties @IBOutlet weak var myDatePicker: UIDatePicker! @IBOutlet weak var deliveryTimeLabel: UILabel! @IBOutlet weak var deliveryDelayPreset: UISegmentedControl! var delay:NSTimeInterval = 0.0 var today = true //MARK: - Target Actions @IBAction func myDateView(sender: UIDatePicker) { printDate(delayTime()) } @IBAction func deliveryDelayPreset(sender: UISegmentedControl) { let index = sender.selectedSegmentIndex let delayString:NSString = sender.titleForSegmentAtIndex(index)! delay = delayString.doubleValue * 60 //convert minutes to seconds printDate(delayTime()) } @IBAction func deliveryDate(sender: UISegmentedControl) { let index = sender.selectedSegmentIndex today = index == 0 if today { myDatePicker.datePickerMode = .Time } else{ myDatePicker.datePickerMode = .DateAndTime } printDate(delayTime()) } //MARK: - Instance Methods func delayTime() -> NSDate{ myDatePicker.minimumDate = NSDate() let pickerTime = myDatePicker.date return pickerTime.dateByAddingTimeInterval(delay) } func printDate(date:NSDate){ let dateFormatter = NSDateFormatter() var theDateFormat = NSDateFormatterStyle.NoStyle if !today { theDateFormat = .ShortStyle } let theTimeFormat = NSDateFormatterStyle.ShortStyle dateFormatter.dateStyle = theDateFormat dateFormatter.timeStyle = theTimeFormat deliveryTimeLabel.text = "Delivered at: " + dateFormatter.stringFromDate(date) } //MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() myDatePicker.datePickerMode = UIDatePickerMode.Time let currentDate = NSDate() myDatePicker.minimumDate = currentDate myDatePicker.date = currentDate //defaults to this but.. } }
Just an attempt at a comment.
Steve Lipton
The web site for tutorials on Swift, iOS and the Raspberry Pi: Making App Pie Apps now available in the iTunes App Store: http://itunes.com/apps/ *stevenlipton*
Reblogged this on Dinesh Ram Kali..
Couldn’t find a command printDate(delayTime()) ? Do I have to import smth. before?
Look at lines 41-61 in the Whole Code. Those are both instance methods discussed in the lesson. The method
delayTime
returns aNSDate
where we add the current time to our delay. The methodprintDate
usesNSFormatter
to format the date nicely.Yeah, now everything is clear. Thanks Steven!
You are welcome.
Hi , can you please explain today = index == 0
Let’s put it in context.
We have this as a property
Which is a Boolean flag we use to say we are delivering today or not. Then we have these three lines:
We are in the action for changing the segmented control. We get the index from the control in the second line, and check if it is zero in the thrid. If so its today and
today
will be true. You might make this long form like thisBut since today is Boolean, and
index == 0
returns a Boolean, you can just assign the value of theindex == 0
like thisMake sense?
Thank you Steven Lipton, it is clear to me now.
No worries. It looks wierd if you haven’t done a lot with boolean values.
Hi thank you for the wonderful lesson,i am new to programming and recently started learning “Swift language” through Make App Pie. After this tutorial i wanted to test my knowledge by making an app like “Alarm Clock”. Initially i thought that i can assign datepicker.date to notification date but soon i realize that datepicker is not giving the exact value. I had no idea how to do it but after a good amount of research on the web i got some understanding about how to implement. I started making the app but now i stuck with a problem. I created a date picker to set the alarm and i used NSDateFormeter function to extract the string from the date. and i made an array with that string and used dateFromComponents to make a date. When i try to print them, the array giving correct numbers but after making date with it the date is coming out different. Please help me to solve this. Thank you very much for help.
This is my code link.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mytest.swift
hosted with ❤ by GitHub
I am i little overwhelmed right now, so i am afraid i cannot help at this time. I approved your comment and if anyonereding this can help you they certainly are welcome to. My apologies.
Pingback: EVERYTHING YOU WANT – Swift resources | swiftioscodetutorial
Pingback: How MakeAppPie.com is Updating to Swift 2.0 | Making App Pie
Pingback: How to Use NSUserDefaults in Swift. | Making App Pie
Thank you!!!
UIDatePicker with UIToolBar in Swift : http://iosdevcenters.blogspot.com/2016/03/ios9-uidatepicker-example-with.html