A simple iOS8 PopDatePicker

In this article we will build a simple iOS DatePicker popping up from a text field. This will be the final result:

popDatePickerPreview

We will implement it as a popover also on the iPhone, thanks to the new iOS8 Presentation Controller features.

The PopDateViewController

We begin with a simple view controller (PopDateViewController) containing the UIDatePicker and a UIButton “OK” (plus relative auto layout). Let us add a new interface file (“PopDateViewController.xib”) to our project with a single view containing these two elements inside:

popdatepicker_xib

The view controller class owner of this XIB loads it using the init method:

and declares two outlets that handles the UIDatePicker and the UIButton:

We used a XIB instead of the storyboard in order to separate the logic of the Picker we are building from the logic of the hosting application (we don’t want to pollute the app storyboard with our stuff).

The PopDatePicker Interface

The app using our picker will not deal with PopDateViewController directly but via an intermediate object that hides the inner details . This object is the PopDatePicker and it is also responsible to show the PopDateViewController inside a popover.

It has two public methods:

It initializes the PopDatePicker and bind it to a UITextField object.

This shows the picker in the view controller passed as first parameter, with an initial value (initDate)  and a closure that is invoked when the user choose a date, the closure must be of type:

We declare the methods as public because we are thinking to build this PopDatePicker as a separate framework in the future (next article in short time).

The closure passes in two parameters: the new date choosen and the UITextField bound to the picker. Note that the second parameter seems redundant because the caller already knows the text field (probably it is one of its property). We pass it just to avoid the use of self (and weak capture list) inside the client closure (life is better without strong reference cycles!).

The PopDatePicker communicates with the PopDateViewController using the protocol/delegate pattern (see DataPickerViewControllerDelegate in the project for details).

PopDateViewController presentation

Our goal now is to present the PopDateViewController as a popover popping up from the text field rect.

In the init method of PopDatePicker we instantiate the PopDateViewController:

And we present it in the pick method:

self.popover is a property of ours and is an instance of the iOS8 new  UIPopoverPresentationController class. We use it to configure the presentation and … to be its delegate.

Adaptive presentation?

Yes, in iOS8 all is “adaptive”. Also the presentation behaves differently depending on the screen size or, better, the class size. So for example a popover presentation becomes a full screen modal presentation if we are on the iPhone.

But it is possible to override this behavior implementing a UIPopoverPresentationControllerDelegate method that ask us the adaptive presentation style to use. This is our implementation:

We are telling to the popover presentation controller that the presentation must be not adaptive at all and so it uses the popover also on the iPhone.

 How to use the picker

In the app view controller we set ourself as the delegate of the UITextField and use this code to show the picker when the user taps:

resign is a method that calls resignFirstResponder() to all the Text Field in the controller (optimization: if you keep track of the current first responder, call resignFirstResponder only to that field).

For all the details about the code see the github related project, and enjoy it.

In the next article we will transform the PopDatePicker in a separate framework to include and use in our app.

Valerio Ferrucci

Valerio Ferrucci (valfer) develops software on Apple Macintosh since the 1990s until today for MacOS, OSX and, since some years, iOS. He is also a Web (PHP/MySQL/JS/CSS) and Android Developer.

More Posts - Website

Follow Me:
TwitterFacebookLinkedIn

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

37 thoughts on “A simple iOS8 PopDatePicker”

  1. Hi Valerio, nice tutorial. It matches, or I should say extends precisely what I would like to do. Unfortunately, I’m struggling to understand a few concepts in your code, and hope you might answer some potentially dumb questions.

    Firstly, could you explain how and why you use the typealias? When you write ” self.textField = forTextField ” are you assigning dobTextField to self.textField? When / where is a value assigned to newDate; and when / where is dataChanged initiated and updated?

    Secondly, if textFieldShouldBeginEditing resigns the first responders and returns false, should the date picker disappear and disallow editing? The opposite seems to happen…

    1. Hi Christopher,
      thank you for appreciating. Don’t forget to see the complete github project for details (https://github.com/valfer/PopDatePickerApp):

      – typealias is not mandatory, it is only a way to name a type with something more convenient for you. Besides, I don’t want to repeat the type “(newDate : NSDate, forTextField : UITextField)->()” every time I use it (Don’t Repeat Yourself).

      – yes, I save the textField “owner” of the PopDatePicker in a property of mines, because then I need it in other methods of the class.

      – value is assigned to newDate in the method “okAction” of PopDateViewController (See the complete github project)

      – dataChanged is inited in the “pick” method (in the article is line 9): self.dataChanged = dataChanged // save the closure

      – in textFieldShouldBeginEditing I return false to avoid the keyboard appearing. Instead of it we show the DatePicker (“popDatePicker!.pick…”).

  2. Sorry for my ignorance but I really don’t get what goes where, I’m quite new to Swift! Could you please help me?

  3. I have this working, kind of… When i press in the input text field where I want the date picker to show, it shows, but it only has Month and day. It is like it is pushed to the right and the year is not showing. Any ideas on how i can change the date picker x position in the pop up view?

      1. ahhhh Did not think of that… I will have to re-add everything. I will try that and let you know. Thanks for the reply!

  4. Hi Valerio,

    I installed a new version of Xcode 6.3 with swift 1.2.

    The Project has crashed , gives the following error:

    Cannot invoke ‘pick’ with an argument list of type ‘(myViewController, initDate: NSDate?, dataChanged: (NSDate, UITextField) -> ())’

    any solution?

  5. It works like charm thanks. But it has a small issue : When I add more text fields, the date picker appears every time I press tab to move to the next text field!

  6. Hey man awesome tutorial. One thing though, I’m getting an error at the line that has
    forTexxtField.text = newDate.ToDateMediumString()
    I don’t see this function popping up anywhere else in you documents, and if I searched correctly in apple’s documentation, it isn’t there either (in fact, if I google that function the only results are to this tutorial). Maybe I’m too sleepy at the moment and just missed something, though not according to ctrl + f ahahahaa. Anyway thanks for the tutorial I was in so much need for it, hopefully you can help me with this error and make it work. Thank you very much!

  7. I was wondering if there was an easy way to have multiple popDatePicker on a single form, so for example I could separate date and time into different textfields?

  8. hi, tq for great tutorial. its works for datepicker, but if i use for pickerview with the same concept, i stucked at
    public typealias PopDatePickerCallback = (newDate : NSDate, forTextField : UITextField) -> ()

    the func at above its purpose for convert date to string?

    if i not use date for pickerview , just use string, it still need func above?

    1. my code :

      else if(textField === pickerTextField){
      resign()
      let initItem : NSString? = pickerTextField.text
      let dataChangedCallback : PopPicker.PopPickerViewCallBack = { (newItem : NSString, forTextField : UITextField) -> () in
      forTextField.text = newItem as String
      }
      PopPicker.pick(self, initItem : initItem, dataChanged: dataChangedCallback)

      return false
      }

      why got error : Extra argument ‘initItem’ in call
      can you help?

    1. Yes, you need the callback to notify the caller class that a new string has been choosed, something like:

      public typealias PopStringPickerCallback = (newString : String, forTextField : UITextField) -> ()

  9. I am trying to use your code to show a datepicker. However, my project is written in Objective-C and I am not sure what I am doing wrong since the popover does not show.

    This is my code for showing date picker:
    – (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
    {
    if(textField == _startDateTF){
    [_startDateTF resignFirstResponder];
    NSLog(@”%@”, @”shouldBeginEditing called”,);
    [datePicker pick:self initDate:[NSDate date] dataChanged:^(NSDate * set_date, UITextField * tf) {
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@”dd.MM.yyyy”];
    NSString *formatedDate = [dateFormatter stringFromDate:set_date];
    tf.text = formatedDate;
    }];
    return false;
    }
    else
    return YES;
    }

    Initialization:
    – (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    datePicker = [datePicker initForTextField:_startDateTF];
    }

    My code compiles successfully but the popover is not shown. Keyboard is blocked tho. Do you have any ideas what should I do?

    1. Is the textFieldShouldBeginEditing ever called?
      I see that you did not set the delegate of _startDateTF to self in viewDidLoad, i.e.:

      _startDateTF.delegate = self

      (perhaps you set it in the storyboard?)

      1. First of all, thank you for your reply.

        Your assumptions are correct, I set the delegate in storyboard and textFieldShouldBeginEditing does get called.

        I put a simple println line of code in PopDatePicker’s init method and noticed that is does not get called.

        I checked my code again and I think the problem might be at this line:
        datepicker = [datepicker initForTextField:_startDateTF];

        I compared it to your example code and it looks like you are initializing with forTextField on PopDatePicker class, not the object of the class. Your code:
        popDatePicker = PopDatePicker(forTextField: dobTextField)

        I tried the same approach, but if I use
        datepicker = [PopDatePicker initForTextField:_startDateTF];
        i get a “no known class method” error. Do you know what should I do?

        1. Do you use the swift class PopDatePicker, or you have rewritten the PopDatePicker class completely in objective-C? If you are using the swift class be sure to include:

          #import "[NAME OF YOUR PROJECT]-swift.h"

          then this should compile:

          datepicker = [[PopDatePicker alloc] initForTextField:_startDateTF];

          (here details about calling swift from obj-c)

          1. The “only” thing missing was
            [PopDatePicker alloc] .

            What can I say, I am new to iOS developing.

            Thank you so much!

  10. Hi Valerio,
    First of all, thanks for sharing very useful tutorial.

    I want to show this datepicker popup when table view cell is clicked. I am not pro in ios programming and I couldn’t find my way to do it.

    Can you please tell me the steps needed to show this popup in table view cell click.

    Thank you!

  11. Hi Valerio,
    Your tutorial is really nice but i am new to Swift so i need some more help from you… 🙂 I want to implement the same in more than one textfield one being a date field same as yours and the other one is a time field. Please help me to implement the same, i am using below condition.

    else if (textField === journeyTimeTextField) {
    resign()

    let formatter = NSDateFormatter()
    // formatter.dateStyle = .NoStyle
    formatter.timeStyle = .ShortStyle
    let initDate : NSDate? = formatter.dateFromString(journeyTimeTextField.text)

    let dataChangedCallback : PopDatePicker.PopDatePickerCallback = { (newDate : NSDate, forTextField : UITextField) -> () in

    // here we don’t use self (no retain cycle)
    forTextField.text = (newDate.ToDateMediumString() ?? “?”) as String

    }

    popDatePicker!.pick(self, initDate: initDate, dataChanged: dataChangedCallback)
    return false
    }
    else {
    return true
    }

    }

    Also, What should come here so that the time field loads the content on its own field instead of date field.

    popDatePicker = PopDatePicker(forTextField: journeyDateTextField)
    journeyDateTextField.delegate = self

  12. hi Valerio, ive implemented your popdatepicker code to my app and works all fine except for small problem. when viewcontroller is instantiated first time the popdate picker works properly. the date format displays correctly (ie date rather than time) and i can save and edit the date. If however i pop the viewcontroller off the navigation stack and then return to it and try to edit the date field again – the date picker appears in time format (ie Today 9:00 AM instead of the date format). the popdatepicker is configured in the viewcontroller viewdidload method as fols

    //create popupdatepicker for datefield – links the popup to an indiv datefield
    let date = NSDate()
    let cal = NSCalendar.currentCalendar()
    let components = cal.components([.Year, .Month, .Day], fromDate: date)

    let currYear = components.year
    let minYear = currYear – 100
    components.setValue(minYear, forComponent: .Year)
    let minDate = cal.dateFromComponents(components)

    popDatePicker = PopDatePicker(forTextField: dobField, pickerMode: UIDatePickerMode.Date, minInterval: nil, maxDate: nil, minDate: minDate)

    any ideas??

  13. Hi

    I need help, I’m trying put 2 dobTextFields in to my project obviously with different TextField name. How do I impliment this?

  14. Beatiful post my friend. Implemented in my project i get this exception:

    UIViewControllerHierarchyInconsistency’, reason: ‘A view can only be associated with at most one view controller at a time!

    1. This might be a few months too late, but if anyone else has this problem: make sure your .xib contains a View, not a ViewController. Otherwise, loading the .xib creates a View that’s already associated with the ViewController, and presenting that view in the manner described in this article would cause the issue you experienced.

  15. Hey Valerio,
    How Can we make the same thing for time selection also. if i have two textfields and i wanted select date from one textfield and time from another?

Leave a Reply to Christopher Dyer Cancel reply

Your email address will not be published. Required fields are marked *