Dailycode.info

Short solution for short problems

iOS : Show datepicker from UITextfield (Extra Generic function for easy creation below using Categories)

Simple thing to ask and luckily, reasonable simple solution. You can add some code on the Editing Did Begin of your text box:

 

- (IBAction)txtFromClicked:(id)sender

{

    UIViewController* popoverContent = [[UIViewControlleralloc] init]; //ViewController

    

    UIView *popoverView = [[UIView alloc] init];   //view

    popoverView.backgroundColor = [UIColor blackColor];

    

    UIDatePicker *datePicker=[[UIDatePickeralloc]init];//Date picker

    datePicker.frame=CGRectMake(0,44,320, 216);

    datePicker.datePickerMode = UIDatePickerModeDate;

    if (self.txtFrom.text.length>0)

    {

        @try {

            NSDateFormatter * formatter = [[NSDateFormatter alloc]init];

            [formatter setDateFormat:@"dd/MM/yyyy"];

            NSDate * dateToSet = [formatter dateFromString:txt.text];

            datePicker.date = dateToSet;

        }

        @catch (NSException *exception) {

           //if the date is in wrong format, set date to today

           datePicker.date = [NSDate date];

        }

    }

    [datePicker setMinuteInterval:1];

    [datePicker setTag:10];

    [datePicker addTarget:selfaction:@selector(Result:) forControlEvents:UIControlEventValueChanged];

    [popoverView addSubview:datePicker];

    

    popoverContent.view = popoverView;

    self.popoverController = [[UIPopoverControlleralloc] initWithContentViewController:popoverContent];

    self.popoverController.delegate=self;

    

    [self.popoverControllersetPopoverContentSize:CGSizeMake(320, 264) animated:NO];

    [self.popoverControllerpresentPopoverFromRect:self.txtFrom.frameinView:self.viewpermittedArrowDirections:UIPopoverArrowDirectionUpanimated:YES];

}

 

- (void) Result : (id) sender

{

    NSDate * dateSelected = ((UIDatePicker *) sender).date;

    NSDateFormatter * formatter = [[NSDateFormatteralloc]init];

    [formatter setDateFormat:@"dd/MM/yyyy"];

    self.txtFrom.text = [formatter stringFromDate:dateSelected];

}

 
This code does the trick. It will also set the date in the picker if a date was selected before. What it does is simply show the date picker in a popover for the frame of the textfield. The result method has the sender date picker and there you can find the selected date.

To make this code a little more general, because you will use it probably more then once, I made this into a static method in a general class:

+ (UIPopoverController *) CreatePopOverControllerWithDatePickerForTextField: (UITextField *) txt andDelegate: (id) del

{

    UIPopoverController * cont;

    

    UIViewController* popoverContent = [[UIViewControlleralloc] init]; //ViewController

    

    UIView *popoverView = [[UIView alloc] init];   //view

    popoverView.backgroundColor = [UIColor blackColor];

    

    UIDatePicker *datePicker=[[UIDatePickeralloc]init];//Date picker

    datePicker.frame=CGRectMake(0,44,320, 216);

    datePicker.datePickerMode = UIDatePickerModeDate;

    if (txt.text.length>0)

    {

       @try {

            NSDateFormatter * formatter = [[NSDateFormatter alloc]init];

            [formatter setDateFormat:@"dd/MM/yyyy"];

            NSDate * dateToSet = [formatter dateFromString:txt.text];

            datePicker.date = dateToSet;

        }

        @catch (NSException *exception)

        {

            //if the date is in wrong format, set date to today

            datePicker.date = [NSDate date];

        }

    }

    [datePicker setMinuteInterval:1];

    [datePicker setTag:10];

    [datePicker addTarget:del action:@selector(Result:) forControlEvents:UIControlEventValueChanged];

    [popoverView addSubview:datePicker];

    

    popoverContent.view = popoverView;

    cont = [[UIPopoverControlleralloc] initWithContentViewController:popoverContent];

    cont.delegate=del;

    [cont setPopoverContentSize:CGSizeMake(320, 264) animated:NO];

    return cont;

}

Now you can simply create the date picker popover providing the textfield and delegate like this:

@interfacePlanningVC ()

@property (strong, nonatomic) UIPopoverController * popoverController;

@end

 

@implementation PlanningVC

 

@synthesize popoverController;

 

- (IBAction)txtFromClicked:(id)sender

{

    self.popoverController = [GeneralMESFunctionsCreatePopOverControllerWithDatePickerForTextField:self.txtFromandDelegate:self];

    [self.popoverControllerpresentPopoverFromRect:self.txtFrom.frameinView:self.viewpermittedArrowDirections:UIPopoverArrowDirectionUpanimated:YES];

}

 

...

 

So that did the trick, or so I thought. When I tried to use a second text box, I already had a problem in my delegate function, because there I literally assing the value to a specific text box. And in this method, I do not really know from which text box it is coming from. So I solved this in a generic way using categories. I created a category of a date picker and in the method I tell the date picker from which control it is coming from. Then when the Result method is called (Value of the date picker is changed) I can simply ask the date picker from which control its coming from. So first I needed to make a category of UIDatePicker extending the date picker with an extra property if type id. There I store the control:

 

//

//  UIDatePicker+MESUIDatePicker.h

//  GalvaSFIApp

//

//  Created by Mark Deraeve on 10/01/13.

//

 

#import <UIKit/UIKit.h>

 

@interface UIDatePicker (MESUIDatePicker)

@property (retain, nonatomic) id usedField;

@end

 

//

//  UIDatePicker+MESUIDatePicker.m

//  GalvaSFIApp

//

//  Created by Mark Deraeve on 10/01/13.

//

 

#import "UIDatePicker+MESUIDatePicker.h"

#import <objc/runtime.h>

 

static char const * const ObjectTagKey ="ObjectTag";

 

@implementation UIDatePicker (MESUIDatePicker)

@dynamic usedField;

 

- (id) usedField

{

    returnobjc_getAssociatedObject(self, ObjectTagKey);

}

 

- (void)setUsedField:(id) usedField

{

    objc_setAssociatedObject(self, ObjectTagKey, usedField, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

 

@end

 

It looks a bit odd, but because it is a category, we have to use the c library to associate the property to the instance of the object. Using id provides us freedom to store whatever kind of control in there. It doesn't have to be a UITextBox.

 

So now I tuned my generic date picker popover creation method to use this category and add the sender textbox to the date picker:

 

#import "UIDatePicker+MESUIDatePicker.h"

 

@implementation GeneralMESFunctions

 

+ (UIPopoverController *) CreatePopOverControllerWithDatePickerForTextField: (UITextField *) txt andDelegate: (id) del

{

    UIPopoverController * cont;

    

    UIViewController* popoverContent = [[UIViewControlleralloc] init]; //ViewController

    

    UIView *popoverView = [[UIView alloc] init];   //view

    popoverView.backgroundColor = [UIColor blackColor];

    

    UIDatePicker *datePicker=[[UIDatePickeralloc]init];//Date picker

    datePicker.frame=CGRectMake(0,44,320, 216);

    datePicker.datePickerMode = UIDatePickerModeDate;

    datePicker.usedField = txt;

    if (txt.text.length>0)

    {

       @try {

            NSDateFormatter * formatter = [[NSDateFormatter alloc]init];

            [formatter setDateFormat:@"dd/MM/yyyy"];

            NSDate * dateToSet = [formatter dateFromString:txt.text];

            datePicker.date = dateToSet;

        }

        @catch (NSException *exception)

        {

            //if the date is in wrong format, set date to today

            datePicker.date = [NSDate date];

        }

    }

    [datePicker setMinuteInterval:1];

    [datePicker setTag:10];

    [datePicker addTarget:del action:@selector(Result:) forControlEvents:UIControlEventValueChanged];

    [popoverView addSubview:datePicker];

    

    popoverContent.view = popoverView;

    cont = [[UIPopoverControlleralloc] initWithContentViewController:popoverContent];

    cont.delegate=del;

    [cont setPopoverContentSize:CGSizeMake(320, 264) animated:NO];

    return cont;

}

 

Finally I can look which control triggered the EventValueChanged event and fill it up, for "completions" sake, I added the whole implementation:

 

//

//  PlanningVC.m

//  GalvaSFIApp

//

//  Created by Mark Deraeve on 29/11/12.

//

 

#import "PlanningVC.h"

#import "GeneralMESFunctions.h"

#import "UIDatePicker+MESUIDatePicker.h"

 

@interfacePlanningVC ()

@property (strong, nonatomic) UIPopoverController * popoverControllerFrom;

@property (strong, nonatomic) UIPopoverController * popoverControllerTo;

@end

 

@implementation PlanningVC

 

@synthesize popoverControllerFrom;

@synthesize popoverControllerTo;

 

- (IBAction)txtToClicked:(id)sender

{

    self.popoverControllerTo = [GeneralMESFunctionsCreatePopOverControllerWithDatePickerForTextField:self.txtToandDelegate:self];

    [self.popoverControllerTopresentPopoverFromRect:self.txtTo.frameinView:self.viewpermittedArrowDirections:UIPopoverArrowDirectionUpanimated:YES];

}

 

- (IBAction)txtFromClicked:(id)sender

{

    self.popoverControllerFrom = [GeneralMESFunctionsCreatePopOverControllerWithDatePickerForTextField:self.txtFromandDelegate:self];

    [self.popoverControllerFrompresentPopoverFromRect:self.txtFrom.frameinView:self.viewpermittedArrowDirections:UIPopoverArrowDirectionUpanimated:YES];

}

 

- (void) Result : (id) sender

{

    UIDatePicker * datePicker = ((UIDatePicker *) sender);

    NSDate * dateSelected = datePicker.date;

    NSDateFormatter * formatter = [[NSDateFormatteralloc]init];

    [formatter setDateFormat:@"dd/MM/yyyy"];

    UITextField * txt = (UITextField *) datePicker.usedField;

    txt.text = [formatter stringFromDate:dateSelected];

}

 

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

    }

    returnself;

}

 

- (void)viewDidLoad

{

    [superviewDidLoad];

// Do any additional setup after loading the view.

}

 

- (void)didReceiveMemoryWarning

{

    [superdidReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

@end

 

You could even use only 1 function to capture the touch down events of both text boxes and pass the sender in stead of the self.txt property.

Here you can see the result:

 

***Edit

If you cannot manage with my example, go and have a look here:

https://github.com/TimCinel/ActionSheetPicker

 

If found this when I was looking for a generic way to show a pickerview in a popup. It works great. I created and extra class that inherits from the AbstractActionSheetPicker. 



blog comments powered by Disqus