I have a date picker in my app and I want to make it so the user can't select a time or date of that which is current, and I want the user to only be able to select a date of two weeks ahead of the current date. How would I accomplish this?
UIDate Picker
just set the maximum date ; you may also set minimum date on the instance (IBOutlet) of the UIDatePicker.
datePicker.minimumDate = Date() // Today
datePicker.maximumDate = Date() + 14 * 86400 // 14 days
How would I get it so that it doesn't show any of the dates before the current day and doesn't show any of the dates after the two weeks?
I don't think it is possible (the design as a Rolodex would look bad if you had for instance just 2 days to display), unless you create a specific UIDatePicker by sublassing it (or create a custom from a UIPicker) ; but that's not worth it.
What you have here is that if you ask for a date after the maximum, it reverts to the maximum. So, it is impossible to set a date beyong the limit. Isn't that enough ?
How would I do it if I create a custom from UIPicker and what would the code look like?
I don't think this is a good idea.
But you would create a picker with 3 components :
- day
- hour
- minutes. // If no minutes, just 2 components
@IBOutlet weak var picker: UIPickerView!
// MARK:- Picker Data Source Methods
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch component {
case 0 : return 15 // From today to today + 14
case 1: return 24 // 00 to 23
case 2: return 12 // If you want to show every 5 minutes
default: return 0
}
}
// MARK:- Picker Delegate Methods
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let today = Date()
let formatter = DateFormatter()
formatter.dateFormat = "EEE. MMM dd" // Sat. Aug 18
switch component {
case 0 :
let currentDay = today + row * 86400
return formatter.string(from: currentDay)
case 1:
return String(format: "%02d", row)
case 2:
let minutes = 5 * row
return String(format: "%02d", minutes)
default: return ""
}
}
Do whatever you need when selected:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let dayOfWPicked = picker.selectedRow(inComponent: 0)
let monthPicked = picker.selectedRow(inComponent: 1)
let dayPicked = picker.selectedRow(inComponent: 2)
}
// use these 3 Int value as needed
}
let currentDay = today + row * 86400
Please please please don’t hard code numbers like this. Not every day has 86400 seconds, and when your app hits one of these days it will fail is mysterious and hard-to-reproduce ways.
To go from day to day reliably you must us
Calendar
APIs (like
date(byAdding:to:)
).
formatter.dateFormat = "EEE. MMM dd" // Sat. Aug 18
You also shouldn’t hard code date formats like this. The user display of dates varies wildly by locale. The correct way to do this is with code like this:
let df = DateFormatter()
df.setLocalizedDateFormatFromTemplate("EEE MMM dd")
// "Mon, Aug 20" in en_US
// "Mon 20 Aug" in en_GB
// "lun. 20 août" in fr_FR
The system will then ensure that all all of your date components are shown, in the right order, with the right separators. It may even add date components, depending on the locale and components you requested.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
Thanks. You're right (as usual) even though I find date manipulation a bit tedious (or needing practice probably).
So better code would be
datePicker.minimumDate = Date() // Today
let df = DateFormatter()
df.setLocalizedDateFormatFromTemplate("EEE MMM dd")
let calendar = Calendar.current
let today = Date()
let comps = DateComponents(day: 3)
datePicker.maximumDate = calendar.date(byAdding: comps, to: today)
So better code would be
Right.
My only other comment is that this:
let today = Date()
always seems suspect to me, because the
Date
value in
today
doesn’t represent a day, it represents a specific point in time in the current day. In situations like this I tend to do the following:
I work primarily in date components, only converting to
values when the API requires it.Date
When that’s required, I pin the ‘time’ parts of the date components to midday (that is, set
to 12,.hours
to 0, and.minutes
to 0) so I’m always working with midday values..seconds
This last part prevents me from accidentally hitting a time discontinuity.
IMPORTANT Using midnight values is very bad, because some time zones do their daylight savings time change at midnight! If you ever get a calendar-related bug report from someone in Brazil that’s having problems around spring or autumn, you’ve likely hit this problem.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"