Search code examples
iosswiftuikituidatepicker

Is it possible to have UIDatePicker work with start and end time?


Currently I have a UIDatePicker working like below:

enter image description here

But I'd like to make it work like below with start and end time? enter image description here

Has anyone accomplished this customization on UIDatePicker?

The app that uses the UIDatePicker in this way is https://www.zeel.com/

Thanks!


Solution

  • While the answers of Rikn and Kayla point me in the right direction, they don't have the part of getting the dates as in the image I provided in the question so that's why I will put here the code how I did it.

    Result:

    Code:

    Properties

    class ViewController: UIViewController {
      @IBOutlet weak var pickerView: UIPickerView!
      
      var days = [Date]()
      var startTimes = [Date]()
      var endTimes = [Date]()
      
      let dayFormatter = DateFormatter()
      let timeFormatter = DateFormatter()
      
      override func viewDidLoad() {
        super.viewDidLoad()
        dayFormatter.dateFormat = "EE d MMM"
        timeFormatter.timeStyle = .short
    
        days = setDays()
        startTimes = setStartTimes()
        endTimes = setEndTimes()
      }
    }
    

    Helper methods

    extension ViewController {
      func getDays(of date: Date) -> [Date] {
        var dates = [Date]()
        
        let calendar = Calendar.current
        
        // first date
        var currentDate = date
        
        // adding 30 days to current date
        let oneMonthFromNow = calendar.date(byAdding: .day, value: 30, to: currentDate)
        
        // last date
        let endDate = oneMonthFromNow
        
        while currentDate <= endDate! {
          dates.append(currentDate)
          currentDate = calendar.date(byAdding: .day, value: 1, to: currentDate)!
        }
        
        return dates
      }
      
      func getTimes(of date: Date) -> [Date] {
        var times = [Date]()
        var currentDate = date
        
        currentDate = Calendar.current.date(bySetting: .hour, value: 7, of: currentDate)!
        currentDate = Calendar.current.date(bySetting: .minute, value: 00, of: currentDate)!
        
        let calendar = Calendar.current
        
        let interval = 60
        var nextDiff = interval - calendar.component(.minute, from: currentDate) % interval
        var nextDate = calendar.date(byAdding: .minute, value: nextDiff, to: currentDate) ?? Date()
        
        var hour = Calendar.current.component(.hour, from: nextDate)
        
        while(hour < 23) {
          times.append(nextDate)
          
          nextDiff = interval - calendar.component(.minute, from: nextDate) % interval
          nextDate = calendar.date(byAdding: .minute, value: nextDiff, to: nextDate) ?? Date()
          
          hour = Calendar.current.component(.hour, from: nextDate)
        }
        
        return times
      }
      
      func setDays() -> [Date] {
        let today = Date()
        return getDays(of: today)
      }
      
      func setStartTimes() -> [Date] {
        let today = Date()
        return getTimes(of: today)
      }
      
      func setEndTimes() -> [Date] {
        let today = Date()
        return getTimes(of: today)
      }
      
      func getDayString(from: Date) -> String {
        return dayFormatter.string(from: from)
      }
      
      func getTimeString(from: Date) -> String {
        return timeFormatter.string(from: from)
      }
    }
    

    Picker Delegate

    extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
      func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 3
      }
      
      func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        switch component {
        case 0:
          return days.count
        case 1:
          return startTimes.count
        case 2:
          return endTimes.count
        default:
          return 0
        }
      }
      
      func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        var label: UILabel
        
        if let view = view as? UILabel {
          label = view
        } else {
          label = UILabel()
        }
        
        label.textColor = .black
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 15)
        
        var text = ""
        
        switch component {
        case 0:
          text = getDayString(from: days[row])
        case 1:
          text = getTimeString(from: startTimes[row])
        case 2:
          text = getTimeString(from: endTimes[row])
        default:
          break
        }
        
        label.text = text
        
        return label
      }
    }