Skip to content

Instantly share code, notes, and snippets.

@andykog
Last active August 29, 2015 14:22
Show Gist options
  • Save andykog/f9ad1a57757e856a8afc to your computer and use it in GitHub Desktop.
Save andykog/f9ad1a57757e856a8afc to your computer and use it in GitHub Desktop.
iOS Date Picker with year, month, day and hours segments
//
// DeadlinePicker.swift
// essayshark
//
// Created by Andrey Kogut on 6/1/15.
// Copyright (c) 2015 ONE FREELANCE LTD. All rights reserved.
//
import UIKit
import ReactiveCocoa
class DeadlinePicker: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
private enum Type {
case Years, Months, Days, Hours
}
unowned let datePicker: UIPickerView
unowned let targetDate: MutableProperty<NSDate>
private let minDate: NSDate
private let minDateComponents:NSDateComponents
private let selectedDateComponents:NSDateComponents
private var selectedDate: NSDate {
return NSCalendar.currentCalendar().dateFromComponents(self.selectedDateComponents)
?? self.minDate // just in case
}
init(picker: UIPickerView, minimumHours: Int, targetDate: MutableProperty<NSDate>) {
self.datePicker = picker
self.targetDate = targetDate
self.minDate = NSDate(timeIntervalSinceNow: NSTimeInterval(60 * 60 * minimumHours))
self.minDateComponents = NSCalendar.currentCalendar().components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay | .CalendarUnitHour, fromDate: minDate)
self.selectedDateComponents = self.minDateComponents
super.init()
self.datePicker.delegate = self;
self.datePicker.dataSource = self;
self.pickDate(self.targetDate.value)
}
private func pickDate(date: NSDate) {
let targetDateComponents = NSCalendar.currentCalendar().components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay | .CalendarUnitHour, fromDate: date)
if let yearIndex = self.indexForValue(ofType: .Years, closestNotSmallerThen: targetDateComponents.year) {
self.datePicker.selectRow(yearIndex, inComponent: 0, animated: false)
self.pickerView(self.datePicker, didSelectRow: yearIndex, inComponent: 0)
}
if let monthIndex = self.indexForValue(ofType: .Months, closestNotSmallerThen: targetDateComponents.month) {
self.datePicker.selectRow(monthIndex, inComponent: 1, animated: false)
self.pickerView(self.datePicker, didSelectRow: monthIndex, inComponent: 1)
}
if let dayIndex = self.indexForValue(ofType: .Days, closestNotSmallerThen: targetDateComponents.day) {
self.datePicker.selectRow(dayIndex, inComponent: 2, animated: false)
self.pickerView(self.datePicker, didSelectRow: dayIndex, inComponent: 2)
}
if let hourIndex = self.indexForValue(ofType: .Hours, closestNotSmallerThen: targetDateComponents.hour) {
self.datePicker.selectRow(hourIndex, inComponent: 3, animated: false)
self.pickerView(self.datePicker, didSelectRow: hourIndex, inComponent: 3)
}
}
private func indexForValue(ofType type: Type, closestNotSmallerThen desiredValue: Int) -> Int? {
var result:(index: Int, value: Int)?
for (index, value) in enumerate(self.values(forType: type)) {
if value >= desiredValue {
if result == nil || value <= result!.value {
result = (index: index, value: value)
}
}
}
return result?.index
}
private func values (forType type: Type) -> [Int] {
var result = [NSDate]()
switch type {
case .Years:
var dateComponents = NSDateComponents()
return (0...10).map {
dateComponents.setValue($0, forComponent: NSCalendarUnit.CalendarUnitYear);
return NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitYear, fromDate: NSCalendar.currentCalendar().dateByAddingComponents(dateComponents, toDate: self.minDate, options: nil) ?? self.minDate).year
}
case .Months:
if let months = NSCalendar.currentCalendar().rangeOfUnit(.CalendarUnitMonth, inUnit: .CalendarUnitYear, forDate: self.selectedDate).toRange()?.map({ $0 }) {
let selectedDateYearsDiff = NSCalendar.currentCalendar().components(.CalendarUnitYear, fromDate: NSDate(), toDate: self.selectedDate, options: nil).year
if selectedDateYearsDiff < 1 {
return months.filter { $0 >= NSCalendar.currentCalendar().components(.CalendarUnitMonth, fromDate: self.minDate).month }
} else {
return months
}
} else {
return []
}
case .Days:
if let days = NSCalendar.currentCalendar().rangeOfUnit(.CalendarUnitDay, inUnit: .CalendarUnitMonth, forDate: self.selectedDate).toRange()?.map({ $0 }) {
let selectedDateMonthsDiff = NSCalendar.currentCalendar().components(.CalendarUnitMonth, fromDate: NSDate(), toDate: self.selectedDate, options: nil).month
if selectedDateMonthsDiff < 1 {
return days.filter { $0 >= NSCalendar.currentCalendar().components(.CalendarUnitDay, fromDate: self.minDate).day }
} else {
return days
}
} else {
return []
}
case .Hours:
let selectedDateDaysDiff = NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitDay, fromDate: NSDate(), toDate: self.selectedDate, options: nil).day
if selectedDateDaysDiff < 1 {
let currentHour = NSCalendar.currentCalendar().components(NSCalendarUnit.CalendarUnitHour, fromDate: minDate).hour
return (currentHour...23).map { $0 }
} else {
return (0...23).map { $0 }
}
}
}
private let dFormatter: NSDateFormatter = {
let dFormatter = NSDateFormatter()
dFormatter.dateFormat = "MMM dd, YYY"
return dFormatter
}()
// MARK: - UIPicker data source & delegate
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 4
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch component {
case 0:
return self.values(forType: .Years).count
case 1:
return self.values(forType: .Months).count
case 2:
return self.values(forType: .Days).count
case 3:
return self.values(forType: .Hours).count
default:
return 0
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
switch component {
case 0:
return String(self.values(forType: .Years)[row])
case 1:
return (NSCalendar.currentCalendar().monthSymbols[self.values(forType: .Months)[row] - 1] as! NSString).substringWithRange(NSRange(location: 0, length: 3))
case 2:
let days = self.values(forType: .Days)[row]
return String(days) + {
switch days {
case 1: return "st"
case 2: return "nd"
case 3: return "rd"
default: return "th"
}
}()
case 3:
let hours = self.values(forType: .Hours)[row]
return String(hours) + (hours == 0 ? "0:01" : ":00")
default:
return nil
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch component {
case 0:
self.selectedDateComponents.setValue(self.values(forType: .Years)[row], forComponent: NSCalendarUnit.CalendarUnitYear)
self.datePicker.reloadComponent(1)
self.datePicker.reloadComponent(2)
self.datePicker.reloadComponent(3)
case 1:
self.selectedDateComponents.setValue(self.values(forType: .Months)[row], forComponent: NSCalendarUnit.CalendarUnitMonth)
self.datePicker.reloadComponent(2)
self.datePicker.reloadComponent(3)
case 2:
self.selectedDateComponents.setValue(self.values(forType: .Days)[row], forComponent: NSCalendarUnit.CalendarUnitDay)
self.datePicker.reloadComponent(3)
case 3:
self.selectedDateComponents.setValue(self.values(forType: .Hours)[row], forComponent: NSCalendarUnit.CalendarUnitHour)
default:
break
}
self.targetDate.put(selectedDate)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment