66

I have this normal UIDatePicker (sorry that it's not in English, please ignore that):

my date picker

But the problem is that it only appears when I'm pressing the Date Picker:

image

And I wanted to know how to show the Date Picker like in the first image (that it's covering all the screen with the blur effect) only with code, so the user won't need to press the Date Picker to show the date selector. Thanks.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41

16 Answers16

15

You can use a datePicker with .compact style and put a UILabel on top of it and give it a background color to hide the datePicker behind it, and make sure the label user interaction is disabled then you should be able to tap on the datePicker and it will show the modal and then the value changed of the datePicker you can use your own date formatter to set the text of the label accordingly, you might also both the datePicker and the label inside a UIView and make it clip to bounds

Amr Mohamed
  • 2,290
  • 4
  • 20
  • 39
  • This is a clever workaround. It worked for me. I had two date pickers side by side. Just had to ensure they were lined up properly so the clicks from the (smaller) labels would pass through. – Dylan Jan 17 '21 at 06:07
  • I was using this workaround and hiding the label when the datePicker was tapped, but iOS 15 caused me to no longer capture taps from the date picker https://stackoverflow.com/questions/69620836/uitapgesturerecognizer-on-uidatepicker-not-working-in-ios-15 – teradyl Oct 25 '21 at 19:28
9

I have the same problem. Im using following hack:

let datePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
datePicker.preferredDatePickerStyle = .compact

// Removes Labels
datePicker.subviews.forEach({ $0.subviews.forEach({ $0.removeFromSuperview() }) })

Then I simply add this date picker on top of a view, which should open this popup.

A. Berisha
  • 148
  • 4
  • 8
    Thanks for the suggestion, but I won't mark this as accepted, because it don't answer all of my question. The user still have to press the Date Picker for it to open. I wanted a solution that instead of the user pressing I will do it only with code. – אורי orihpt Sep 27 '20 at 15:52
  • I think this is the closest one can get to achieve this functionality – Shunan Nov 08 '20 at 08:22
  • I see the labels disappearing, but the calendar doesn't appear. – clearlight Apr 07 '22 at 23:21
9

Unfortunately it's not possible to programatically display the date selector popup, at least not resorting to hacks that might result in your app being rejected:

  • the UIDatePicker control doesn't expose any events that can be programatically triggered via sendActions(for:) - allControlEvents returns an empty option set

  • programatically simulating a user click is officially impossible on iOS - there is no public API to achieve this, all workarounds imply resorting to private API's

  • if the above wouldn't be enough, the UI structure of UIDatePicker is composed of private classes:

    enter image description here enter image description here

The conclusion is that in its current state, the UIDatePicker UI can be controlled only via user interactions. Maybe this will change in the future, for example UIButton's expose actions for triggering the same behaviour as when the user tapped the button, so maybe UIDatePicker will expose such actions in the future.

Cristik
  • 30,989
  • 25
  • 91
  • 127
8

It's impossible to open a UIDatePicker with UIKit14 from within the code

  • UIDatePicker has no API for doing this
  • UIKit doesn't allow us to create a UIEvents or UITouch object which has any impact on UIKit.
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
6

I don't know if this is still valid, but you can make use of the .inline style of new UIDatePicker. Then use your own view and animations to mimic the appearance of the DatePicker from .compact style.

let secondDatePicker = UIDatePicker()
secondDatePicker.preferredDatePickerStyle = .inline
self.view.addSubview(secondDatePicker)

secondDatePicker.translatesAutoresizingMaskIntoConstraints = false
secondDatePicker.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
secondDatePicker.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
pkamb
  • 33,281
  • 23
  • 160
  • 191
4

UPDATE: THIS IS NOT WORKING ANYMORE ON iOS 15.

@Amr Mohamed 's answer is clever by putting a label on the date picker view, but this also causes a glitch when tapping the date picker view, when the date picker vc animates out, you can also see the date picker.

So we need to "hide" the date picker view but also leave it interactive. (We can't just use isHidden = true or alpha = 0, otherwise it's not tappable anymore)

This is how I did in my project:

extension UIView {
    func loopDescendantViews(_ closure: (_ subView: UIView) -> Void) {
        for v in subviews {
            closure(v)
            v.loopDescendantViews(closure)
        }
    }
}

let datePicker = UIDatePicker()
datePicker.preferredDatePickerStyle = .compact
datePicker.loopDescendantViews {
    $0.alpha = $0 is UIControl ? 1 : 0
}

Then you can put this invisible date picker on top of your label/other view. once you tap the label (actually tapped the date picker), the date picker vc animates out.

iAugus
  • 69
  • 1
  • 6
2

You can set the preferred style of the date picker to "inline" from the storyboard. By doing this, you will directly get the full screen for date selection directly.

here is how the date picker looks for preferred style "compact"( the one you have):

date picker with preferred style as compact

vs "inline" ( the one you are looking for):

date picker with preferred style as inline

Gurunath Sripad
  • 1,308
  • 2
  • 14
  • 24
2

Just to be sure we're not missing the obvious here, the .inline style displays the full calendar without the user needing to click a date or time label. With that ability one could arrange any behavior they want with regard to displaying the calendar and selected/default dates, etc...

clearlight
  • 12,255
  • 11
  • 57
  • 75
  • yea we know that. – אורי orihpt Apr 08 '22 at 18:00
  • 1
    It's just that the workarounds & hacks for programmatically triggering it in compact mode are so draconian that one needs to know there's a solid way to achieve it, which I didn't from reading the docs too fast because the default mode is compact. So I thought you *had* to click the label to see the calendar. So even though you know and people who are really focusing on your question and all the details may assume all of this, it is possible some people will benefit from someone pointing it out. In fact I was very close to using a 3rd party datepicker before I found .inline does what I need. – clearlight Apr 08 '22 at 19:20
0

I think the only non-hacky approach is to create a new view controller, add a UIDatePicker instance in it with the embedded style, then present the controller modally or as a popover.

gklka
  • 2,459
  • 1
  • 26
  • 53
0

I am using this extension to set clear colour on every subview of the UIDatePicker and then use a UILabel as suggested by others.


extension UIDatePicker {
  
  private func traverse(view: UIView) {
    for subview in view.subviews {
      self.traverse(view: subview)

      // Setting alpha to 0 disables userInteraction.
      subview.alpha = 0.02
    }
  }
  
  func paintClear() {
    self.traverse(view: self)
  }
  
}


Vinay Jain
  • 1,653
  • 20
  • 28
0

Well, I found a way to make it work for both iOS 14 and iOS 15, but since it reads the hierarchy it may stop working with any upcoming iOS release:

// The callback from your tap gesture or whatever you use:
func handleTapOnView() {
    let datePickerGesture: UIGestureRecognizer? = {
        guard let compactView = datePicker.subviews.first else { return nil }
        // iOS 15
        if let dateLabel = compactView.subviews
                .first(where: { String(describing: type(of: $0)).hasSuffix("DateLabel") }),
           let gesture = dateLabel.gestureRecognizers?.first {
            return gesture
        }
        // iOS 14
        if let gesture = compactView.gestureRecognizers?.last {
            return gesture
        }
        return nil
    }()
    datePickerGesture?.state = .ended
}

For the 'iOS 15' part of the code use .hasSuffix("DateLabel") if you want to focus the date selection part, and .hasSuffix("TimeLabel") for focusing the time selection part.

nalexn
  • 10,615
  • 6
  • 44
  • 48
-1

Define these variables

var datePicker: UIDatePicker!
var datePickerConstraints = [NSLayoutConstraint]()
var blurEffectView: UIView!

Then create a function showDatePicker() that you call to trigger the display of the UIDatePicker. First, create a subview with the same size of his parent View and give it a blur effect. Then add the UIDatePicker, as a subview on top of the blurred view.

   func showDatePicker() {
        datePicker = UIDatePicker()
        datePicker?.date = Date()
        datePicker?.locale = .current
        datePicker?.preferredDatePickerStyle = .inline
        datePicker?.addTarget(self, action: #selector(dateSet), for: .valueChanged)
        addDatePickerToSubview()
    }

    func addDatePickerToSubview() {
        guard let datePicker = datePicker else { return }
        // Give the background Blur Effect
        let blurEffect = UIBlurEffect(style: UIBlurEffect.Style.regular)
        blurEffectView = UIVisualEffectView(effect: blurEffect)
        blurEffectView.frame = self.view.bounds
        blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.view.addSubview(blurEffectView)
        self.view.addSubview(datePicker)
        datePicker.translatesAutoresizingMaskIntoConstraints = false
        centerDatePicker()
        view.bringSubviewToFront(datePicker)
    }

    func centerDatePicker() {
        guard let datePicker = datePicker else { return }
        // Center the Date Picker
        datePickerConstraints.append(datePicker.centerYAnchor.constraint(equalTo: self.view.centerYAnchor))
        datePickerConstraints.append(datePicker.centerXAnchor.constraint(equalTo: self.view.centerXAnchor))
        NSLayoutConstraint.activate(datePickerConstraints)
    }

And finally, once a date a selected in the UIDatePicker

 @objc func dateSet() {
        // Get the date from the Date Picker and put it in a Text Field
        compactDatePicker.date = datePicker.date
        blurEffectView.removeFromSuperview()
        datePicker.removeFromSuperview()
    }

Not entirely sure about the blurr effect so hopefully someone can build on this solution.

Sami
  • 67
  • 7
  • 2
    But there is no `dateTextField`. There is a compact date picker, and the idea is to open it as if the user had tapped it. – matt Oct 04 '20 at 04:25
  • i am not aware of any method to "fake" a user tapping the compact date picker, so i thought with the code above i would fake it by presenting another date picker in inline style then when dismissed, set the value of the original compact date picker to that of the one just presented. – Sami Oct 04 '20 at 14:11
-1

It works to switch to the old style of date picker:

        if (@available(iOS 13.4, *)) {
            [datePicker setPreferredDatePickerStyle:UIDatePickerStyleWheels];
        }
Dmitry
  • 14,306
  • 23
  • 105
  • 189
-2

Date picker changed in iOS v14.0 before that it was an inline wheel. Now in iOS v14.0 what you can do is tell it to be inline and it will show the calendar inline (no popup):

if #available(iOS 14.0, *) {
     datePicker.preferredDatePickerStyle = UIDatePickerStyle.inline         
}

Note: you may need to fix the frame size as the datepicker will look different in iOS 13 (wheel) and iOS 14 (calendar), so you will probably need an "else" for version 13..

Orhan
  • 1,395
  • 13
  • 12
-3

I guess you are using the compact date picker style which is a new feature on iOS 14. And I think you can simply use ‏‏‏‏the inline style instead of compact to show the full calendar UI. And don't forget that the UIDatePicker is just an UIView so you can customize your presentation to implement the blur effect you want.‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏

אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
congnd
  • 1,168
  • 8
  • 13
-4

If you want to continue using the wheels format, you can set the style to wheels on the story board,

enter image description here

or use this code

datePicker.preferredDatePickerStyle = .wheels
אורי orihpt
  • 2,358
  • 2
  • 16
  • 41
Mona
  • 5,939
  • 3
  • 28
  • 33
  • No. Read my question again. – אורי orihpt Nov 06 '20 at 09:55
  • 1
    This not just about your specific question. sometimes this page showed up for me when I searched for a way to continue using wheels, and it will show up on google for others who will searching for the same thing in the future. I posted this answer to help those people. in my answer i have also mentioned this is the solution for people who want to continue using wheels – Mona Nov 14 '20 at 04:14
  • Thanks Mona!, i was looking for this. – Joule87 Apr 05 '21 at 18:11
  • why so many negative upvotes for this answer...answer is perfectly fine if someone is setting date picker on storyboard.. – jayant rawat Jan 16 '22 at 07:28