William Collishaw
William Collishaw

Reputation: 95

How to reset inline iOS UIDatePicker when no date is selected

When using the new inline UIDatePicker (Introduced in iOS 14). How can I reset the calendar to current date when no date is already selected.

When running the following, the displayed calendar will reset to the current date WHEN you have selected another date:

self.datePicker.setDate(Date(), animated: true)

However, if you create a new inline UIDatePicker and start scrolling through months, running this same line of code will not update the view until a specific date is selected by the user.

Upvotes: 0

Views: 2282

Answers (1)

DonMag
DonMag

Reputation: 77433

That's curious...

My guess would be that when using the older "wheel" date picker style, a new date is selected when the wheels stop scrolling... there is no way to "scroll away" from the selected date.

My second guess would be that this issue could be changed ("fixed") in a future iOS update.

Anyway, here is one work-around...

  • get today's date
  • get the selected date from the picker
  • if they are the same, user may have scrolled the months / years, so
    • add one day to "today"
    • set the picker to "tomorrow" (animated)
    • make an async call to set the picker to "today"
  • if the dates are not the same, just set the picker to "today" (animated)

I've only done a quick test of this, so you'll want to thoroughly test it. And probably add some additional checking -- such as what happens if the calendar rolls over midnight while the picker is displayed; how does it look if "today" is the last day of the month; etc.

    // get "today" date
    let today = Date()

    // get selected date
    let pickerDate = self.datePicker.date
    
    // are the dates the same day?
    let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)

    if todayIsSelected {
        // picker has today selected, but may have scrolled months...

        // should never fail, but this unwraps the optional
        guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
            return
        }

        // animate to "tomorrow"
        self.datePicker.setDate(nextDay, animated: true)

        // async call to animate to "today"
        DispatchQueue.main.async {
            self.datePicker.setDate(today, animated: true)
        }
    } else {
        // picker has a different date selected
        //  so just animate to "today"
        self.datePicker.setDate(today, animated: true)
    }
    

Edit - complete example:

class ScratchVC: UIViewController {
    
    let datePicker = UIDatePicker()
    let btn = UIButton()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        if #available(iOS 14.0, *) {
            datePicker.preferredDatePickerStyle = .inline
        } else {
            // Fallback on earlier versions
        }
        
        btn.backgroundColor = .red
        btn.setTitle("Go To Today", for: [])
        btn.setTitleColor(.white, for: .normal)
        btn.setTitleColor(.gray, for: .highlighted)
        
        btn.translatesAutoresizingMaskIntoConstraints = false
        datePicker.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(btn)
        view.addSubview(datePicker)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
            
            datePicker.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.9),
            datePicker.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            datePicker.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            
        ])
        
        btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
        
    }
    
    @objc func didTap(_ sender: Any) {
        
        // get "today" date
        let today = Date()

        // get selected date
        let pickerDate = self.datePicker.date
        
        // are the dates the same day?
        let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)

        if todayIsSelected {
            // picker has today selected, but may have scrolled months...

            // should never fail, but this unwraps the optional
            guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
                return
            }

            // animate to "tomorrow"
            self.datePicker.setDate(nextDay, animated: true)

            // async call to animate to "today" - delay for 0.1 seconds
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
                self.datePicker.setDate(today, animated: true)
            })
        } else {
            // picker has a different date selected
            //  so just animate to "today"
            self.datePicker.setDate(today, animated: true)
        }
        
    }
    
}

Upvotes: 1

Related Questions