Show Menu

Need an iOS Developer?

Submit your 30 day Job Listing for FREE

In part one of this tutorial we’ve seen how to display in-line date picker in a table view with dynamic cells.

If you haven’t already, I recommend that you read the first part to this tutorial: iOS 7 in-line UIDatePicker – Part 1

In this second part we are going to see how to do this for static cells. We will build a view that will add a new person to our list. I will continue from where we left off. If you don’t want to have dependencies from part one you can just create a new project with a single view and ignore the steps that configure the navigation from the people list to the add person view.

At the end of part one we had an application that displayed a list of people in a table view. When a cell is selected a UIDatePicker appeared underneath the selected cell and allowed you to change the birth date for that person.

First we need to add a button to the navigation bar of the people view controller. This button will present us with the add person view that we want to build. Go to Interface Builder and add a UIBarButtonItem. Change the identifier to be “Add”. You should see it as a + now which is what we want. We should also do some cleaning. Let’s get rid of the VCDetailViewController from Interface Builder and the .h and .m project files.

Next add a new table view controller to the storyboard. Before we start configuring this let’s create the .h and .m files for it. Go to File->New->File and create a new Objective C class. Name it VCAddPersonTableViewController and make sure it’s a UITableViewController subclass.

Xcode New Controller

Once you’ve created this class go back to Interface Builder, select the table view controller, go to the Identity Inspector and set the custom class to be VCAddPersonTableViewController. We want to have some navigation options so let’s embed the table view controller in a navigation controller. Make sure that table view controller is selected then go to Editor->Embed In->Navigation Controller.

This should create a navigation controller in the storyboard and add a navigation bar to the table view controller. The add person view controller will be presented modally. We can create a segue from the add button we’ve added in the people view controller to the navigation controller that embeds the add person view controller. Just select the add button, hold Ctrl and drag from the button to the navigation controller. Select modal from the list of options for the Action Segue.

We have a way of presenting the add person view controller but we have no way out of it. We would want to release it when we finished entering details for a person or if we changed our mind about adding a new person. So let’s add two buttons to the navigation bar, one to cancel and one to save. And while we’re at it let’s also add a title to the navigation bar so we know where we are (“Add Person” should do).

We need action methods for the two buttons, so wire up cancelPressed: and savePressed: methods to the bar buttons. We can already implement the cancelPressed: method. We only want to dismiss the view controller when cancel is pressed so the method will look like this:


- (IBAction)cancelPressed:(UIBarButtonItem *)sender {
 
    [self dismissViewControllerAnimated:YES completion:nil];
}

Let’s start configuring the table view now. Select the table view and go to the Attributes Inspector tab. Make the content of the table view “Static Cells”. Because we’re using static cells we won’t need a data source for the table view so go to the Connections Inspector and remove the data source.

In part one of this tutorial we had a prototype for the date picker cell and we inserted and deleted the cell whenever we wanted to. Now let’s choose a different approach. We will always have a date picker cell and instead of deleting it and inserting it we will just show it or hide it. We can hide it by making the height of the cell 0. So let’s go to the table section and select that we need 4 rows.

The first cell of the table view will contain a UITextField that will allow the user to enter its full name. Drag a text field from the Object Library. Change its border style to none in the Attributes Inspector. We don’t need borders, we want it to look like the whole cell can be edited. Let’s create an IBOutlet for the text field called nameTextField. And we need to setup a delegate for it. We’ll make out table view controller its delegate, just Ctrl drag from the text field to the controller and select the delegate outlet. And don’t forget to go to the VCAddPersonTableViewController.h file and mark that it’s conforming to the UITextFieldDelegate protocol.

That’s great, we finished setting up the first cell. The second cell is the one that will allow you to enter a person’s birth date. Tapping on this cell will reveal the date picker. This cell will contain two labels: one with static text to indicate that the date is a birth date and one that will display the selected date. The first label will stay the same so all we need to do is set its text to “Date of birth:”. The second label will contain the value from the date picker and we will need to update this value so let’s create an outlet for it. Drag a label, position it to the right of the first label and create an outlet called birthdayLabel. This should be all the setup we need for it.

The third cell is the cell that contains the date picker. We will show and hide this cell as needed. All you need to do to configure this cell is drag a UIDatePicker into the cell. We will need an outlet for it so that we can setup the default date. We will also need an action method for when the value of the date picker changes. We’ll call the outlet datePicker and the method pickerDateChanged: Because we want to be able to hide the cell we will keep an outlet for the cell too, called datePickerCell. We need to configure the date picker to show the date, no time and we can setup a minimum and maximum date for it. You can setup all this in Attributes Inspector.

Finally, the last cell to be configured will only contain a UITextField for the place of birth. Add an outlet called placeOfBirthTextField and make the view controller the delegate for this text field. Also setup the placeholder text to be “Place of birth”.

This is how everything should look like when we’re finished:

xcode finished

In the VCAddPersonTableViewController.m we need to remove all the methods related to the table view data source. Because we are using static cells we don’t need the data source. Remove the following methods: numberOfSectionInTableView:, tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:. We should now be able to run the project and see the view that we’ve created. Of course it won’t do much for now, but we’re going to fix that.

We need to configure the birthdayLabel to have a default value when we first show the view. We will set this value to now. Let’s create a method called setupBirthdayLabel and call it from viewDidLoad. The method will configure the label like this:


- (void)setupBirthdayLabel {
 
    self.dateFormatter = [[NSDateFormatter alloc] init];
    [self.dateFormatter setDateStyle:NSDateFormatterMediumStyle];
    [self.dateFormatter setTimeStyle:NSDateFormatterNoStyle];
 
    NSDate *defaultDate = [NSDate date];
 
    self.birthdayLabel.text = [self.dateFormatter stringFromDate:defaultDate];
    self.birthdayLabel.textColor = [self.tableView tintColor];
 
    self.selectedBirthday = defaultDate;
}

Notice that we are using a dateFormatter so you will need to declare a property for that.

The date picker cell is visible all the time. We want it to be hidden until we tap on the cell with the birth date. To hide the date picker cell we will make its height 0. We will do this in tableView:heightForRowAtIndexPath: To make things simpler let’s define a constant to hold the row for the date picker cell. We’ll call that kDatePickerIndex. For the row containing the date picker cell we will return a height of 0.


#define kDatePickerIndex 2
 
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    CGFloat height = self.tableView.rowHeight;
 
    if (indexPath.row == kDatePickerIndex){
 
        height =  0.0f;
 
    }
 
    return height;
}

If we run our project now the date picker is gone. Let’s implement the method that will show it or hide it as needed. As expected we will need to do this in the tableView:didSelectRowAtIndexPath:. The only time we have an action to perform is when we select the cell with the date of birth label. When the user taps on that cell we need to show the date picker cell. If the user taps again on it then we need to hide it. So we need a way to know which operation to perform. Let’s define a boolean property to keep track of whether the date picker is showing or is hidden. We’ll call it datePickerIsShowing. Our tableView:didSelectRowAtIndexPath: method will look like this:


-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
    if (indexPath.row == 1){
 
        if (self.datePickerIsShowing){
 
            [self hideDatePickerCell];
 
        }else {            
 
            [self showDatePickerCell];
        }
    }
 
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}

To hide the date picker we are creating a short animation to make it fade away. We are setting the boolean property to NO and we are calling the beginUpdates and endUpdates on the table view so that everything gets refreshed and animates nicely. To show the date picker we are doing the exact opposite by making it fade in.


- (void)showDatePickerCell {
 
    self.datePickerIsShowing = YES;
 
    [self.tableView beginUpdates];
 
    [self.tableView endUpdates];
 
    self.datePicker.hidden = NO;
    self.datePicker.alpha = 0.0f;
 
    [UIView animateWithDuration:0.25 animations:^{
 
        self.datePicker.alpha = 1.0f;
 
    }];
}
 
- (void)hideDatePickerCell {
 
    self.datePickerIsShowing = NO;
 
    [self.tableView beginUpdates];
    [self.tableView endUpdates];
 
    [UIView animateWithDuration:0.25
                     animations:^{
                         self.datePicker.alpha = 0.0f;
    }
                     completion:^(BOOL finished){
                         self.datePicker.hidden = YES;
                     }];
}

There’s one more thing we need to do before we can run the project and see the animation. We need to setup the height for the date picker cell so that it’s 0 only when the date picker is hidden and it’s the height of the date picker otherwise. So let’s change the tableView:heightForRowAtIndexPath: method to do just that


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    CGFloat height = self.tableView.rowHeight;
 
    if (indexPath.row == kDatePickerIndex){
 
        height = self.datePickerIsShowing ? kDatePickerCellHeight : 0.0f;
 
    }
 
    return height;
}

kDatePickerCellHeight is a constant set to the height of the date picker, in our case is 164. With this change we can now see our date picker appearing and disappearing when we tap the date of birth cell. However, if we select a value, the birthdayLabel doesn’t get updated. To fix this we need to implement the pickerDateChanged: method like this:


- (IBAction)pickerDateChanged:(UIDatePicker *)sender {
 
    self.birthdayLabel.text =  [self.dateFormatter stringFromDate:sender.date];
 
    self.selectedBirthday = sender.date;
}

Things look better, but we are still missing a few things. First, if we try to enter some information in the first text field and then tap on the cell that will bring out the date picker we notice that things aren’t looking good. The keyboard doesn’t get dismissed and if you’re testing this on a 3.5 inch device then the date picker will be under the keyboard. And we have the same problem with the text field for the place of birth. So we need to make sure that we dismiss the keyboard when the date picker appears and that we dismiss the date picker when the keyboard will appear. To do this first we need to register for keyboard notifications:


- (void)signUpForKeyboardNotifications {
 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil];
 
}

Call this method from viewDidLoad. The method that gets called when the keyboard will show needs to hide the date picker if it’s visible.


- (void)keyboardWillShow {
 
    if (self.datePickerIsShowing){
 
        [self hideDatePickerCell];
    }
}

To dismiss the keyboard we need to tell the active UITextField to resign first responder. Because we have two text fields on the screen we will use a property to remember which one is the first responder at this moment. Before we show the date picker we need to tell the active text field to resign first responder:


[self.activeTextField resignFirstResponder];

To keep track of which text field is active we will use the textFieldDidBeginEditing: method.


- (void)textFieldDidBeginEditing:(UITextField *)textField {
 
    self.activeTextField = textField;   
}

This solved the problem with the keyboard and the date picker showing at the same time. The last thing we want to do is implement the save button we’ve placed on the navigation bar. We have an action method for it but it’s not implemented yet. What we would like to happen is for the person that we’ve just defined to get added to the people list in the main view controller. To do this we will make the main view controller the delegate for our add person view and send it the details for the person when we press save. The protocol for the delegate will look like this:


@protocol VCAddPersonDelegate 
 
- (void)savePersonDetails:(VCPerson *)person;
 
@end

We’ll define a delegate property in our table view controller like this:


@property  (weak, nonatomic) id delegate;

Make it weak to avoid reference cycles. The savePressed: method will create a new person and pass it to the delegate.


- (IBAction)savePressed:(UIBarButtonItem *)sender {
 
    VCPerson *person = [[VCPerson alloc] initWithName:self.nameTextField.text
                                          dateOfBirth:self.selectedBirthday
                                         placeOfBirth:self.placeOfBirthTextField.text];
 
    [self.delegate savePersonDetails:person];
 
    [self dismissViewControllerAnimated:YES completion:NULL];    
}

This will only work if the VCPeopleViewController set itself as the delegate for the VCAddPersonTableViewController. It would do that just before the segue happens:


-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 
    if ([[segue identifier] isEqualToString:kSegueIdentifier]){
 
        VCAddPersonTableViewController *controller = [[[segue destinationViewController] viewControllers] objectAtIndex:0];
 
        controller.delegate = self;
    }
}

And it should implement the protocol method:


- (void)savePersonDetails:(VCPerson *)person {
 
    [self.persons addObject:person];
 
    NSArray *indexPaths = @[[NSIndexPath indexPathForRow:[self.persons count]-1 inSection:0]];
 
    [self.tableView insertRowsAtIndexPaths:indexPaths
                          withRowAnimation:UITableViewRowAnimationFade];
}

We have fully implemented the controls of our view and we should now be able to add a new person to our list.
[socialRansom file=”https://github.com/costescv/InlineDatePicker/archive/master.zip”]

having issues?

We have a Questions and Answer section where you can ask your iOS Development questions to thousands of iOS Developers.

Ask Question

FREE Download!

Get your FREE Swift 2 Cheat Sheet and quick reference guide PDF download when you sign up to SwiftMonthly


Sharing is caring

If you enjoyed this tutorial, please help us and others by sharing using one of the social media buttons below.


Written by:

Vasilica Costescu is just a girl that loves her iPhone and iOS programming, she graduated from: West University of Timisoara with a degree in Computer Science in 2006. Her blog has awesome articles. Check it out.

Comments

comments