Custom iOS UIDatepicker using UIAppearance
Asked Answered
G

8

13

UNIQLO's new alarm app has a custom UIDatePicker:

custom

And I want to create my own custom UIDatePicker.

I tried to change the appearance of the DatePicker, but looking for UI_APPEARANCE_SELECTOR in the UIDatePicker returns nothing.

Meaning that its not possible to change any values, as per the docs:

To support appearance customization, a class must conform to the UIAppearanceContainer protocol and relevant accessor methods must be marked with UI_APPEARANCE_SELECTOR.

How can change my UIDatePicker's appearance?

Georgia answered 1/6, 2012 at 5:3 Comment(5)
please excuse the HUGE image..Georgia
you want only 2 component in UIDatePicker or just need that white font of text as a customization?Lordly
Customising as much as I can: Font, colour, everything.Georgia
Please take a look I have customized the inner-view of picker, you can change the code as per requirement.Lordly
are you getting any issue in customizing the pickers' content with the method I go ?Lordly
L
28

The API does not provide a way to do this. You can make a pretty convincing replica yourself using a UIPickerView rather than using UIDatePicker.

As the UIDatePicker or UIPickerView don't have the UI_APPEARANCE_SELECTOR and even you can't change UIDatePicker contents' appearance as its UIControl and not having any delegate so it has its native appearance whereas in case of UIPickerView you can change its contents' appearance similar as in UITableView.

#pragma mark -
#pragma mark UIPicker Delegate & DataSource

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 2;
}

// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return 100;
}
//- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:  (NSInteger)component {
     //    return [NSString stringWithFormat:@"%d",row];
//}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {

      UILabel *label= [[[UILabel alloc] initWithFrame:CGRectMake(30.0, 0.0, 50.0, 50.0)] autorelease];
      [label setBackgroundColor:[UIColor clearColor]];
      [label setTextColor:[UIColor blueColor]];
      [label setFont:[UIFont boldSystemFontOfSize:40.0]];
      [label setText:[NSString stringWithFormat:@"%d",row]];

      return label;
}

enter image description here

Check this out.

Lordly answered 4/7, 2012 at 12:34 Comment(0)
M
4

add this method in your implementation file

-(UIView *)pickerViews{

    return ([datePicker.subviews objectAtIndex:0]);
}

-(void)viewDidLoad{

    [super viewDidLoad];

    [[self pickerViews].subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"%@ --- > %i",obj, idx);
    }];
}

when you run your app it will display all the subviews of the datePicker on your console, just choose any index and modify them one by one

modify the viewDidLoad and insert this code

UIView *background = (UIView *)[[self pickerViews].subviews objectAtIndex:0];
background.backgroundColor = [UIColor clearColor];

UIView *wheel2 = (UIView *)[[self pickerViews].subviews objectAtIndex:4];
wheel2.backgroundColor = [UIColor redColor];

UIView *wheel3 = (UIView *)[[self pickerViews].subviews objectAtIndex:11];
wheel3.backgroundColor = [UIColor redColor];

In this case i change the background color of index "0", "4", "11"

Marty answered 10/7, 2012 at 9:43 Comment(0)
T
3

With iOS 5 the UIAppearance protocol was introduced, which lets you customize several UI elements. UIDatePicker happens to conform this protocol, so that is probably the easiest way to do this. That is, if you're willing to support only iOS 5 users. Matthew's option would probably the next best thing, which should also work with older iOS versions.

Theurgy answered 1/6, 2012 at 5:32 Comment(4)
Thanks I forgot about UIAppearanceGeorgia
Sorry Scott, I cant get the UIAppearance to work on the UIDatePicker. Please see edited question.Georgia
This should be a comment, not an answer as it only suggests the use of UIAppearance, and not how to use it in this context.Mettlesome
No, you cannot use appearance to change UIDatePickers graphics. Check my answer for more insight.Manara
M
3

It appears to be quite difficult to change the way a UIDatePicker appears.

The example you provided, in my opinion, is a sophisticated customization of a simple UIPickerView with two columns and probably a simulated infinite scroll (as Apple does in the date picker, and it's quite simple to realize).

You can change little of the UIDatePicker through the UIAppearance proxy, as seen in this example:

UIDatePicker *picker = [UIDatePicker appearance];
picker.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:0.3];

UIView *view;
view = [UIView appearanceWhenContainedIn:[UITableView class], [UIDatePicker class], nil];
view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];

UILabel *label = [UILabel appearanceWhenContainedIn:[UITableView class], [UIDatePicker class], nil];
label.font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
label.textColor = [UIColor blueColor];

Using this piece of code at the start of the application (just try it) you can change the aspect of only a couple of the main components of the date picker.

Labels are impossible to customize (and this test code proves it); apparently they are changed in aspect every time you rotate a column, since they are put inside a custom UITableView controller.

To fully change these appearances you should probably work with private APIs from Apple, that will eventually result in your app being rejected.

If you just need a custom hour-minute picker (as shown in your screenshot), the full customization of appearance is reachable only extending the UIPickerView class or assigning an appropriate delegate to a UIPickerView instance.

Through the

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;

method on the delegate you can make the single element of the picker view appear the way you want. Then you will have to appropriately handle the data source to make the two components of the picker virtually infinite.

Manara answered 4/7, 2012 at 13:58 Comment(2)
I've looked at the UIApperance and still can't see how to change the text color of a UIDatePicker. Does it support that?Handgrip
As I said in the answer, labels are impossible to customize.Manara
S
0

You can implement one yourself using two UIScrollViews, one for hours and one for minutes. You would just need to figure out the contentOffset of each scrollview when it has stopped moving, which off the top of my head would look like this:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  if (!decelerate) {
    // get the offset here
  }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  // also get the offset here
}

Take the value of the content offset and translate it into the hour value and minute value, probably by dividing the y position of the content offset by the 24 or 60 respectively. You will need to make the custom artwork yourself.

Sphingosine answered 1/6, 2012 at 5:24 Comment(3)
Note that pickers have "infinite" scrolling: when you get to the end (say 59), you go back to the beginning. In reality, there really isn't an end and a beginning, but it appears so because of the numbers in there. This is definitely something to keep in mind when customizing UIScrollViews to mimic UIDatePicker behavior.Theurgy
Yes, that is true. I just downloaded the UNIQLO app to check and it would also require faking infinite scrolling (which would, of course, also complicate the math) and would also requiring some tricks using paging to get the scroll view to lock onto a particular position.Sphingosine
Without having thought this through completely, I think a UITableView may be an option. No hard math involved, just lazily keep adding cells as they are being scrolled to. Reusing cells would make for only a small performance hit, and using "scrollToRowAtIndexPath" would be useful for the position locking mechanism.Theurgy
R
0

To change date picker color:

[datePickerView setValue:self.pickerLabelColor forKey:@"textColor"];

And to change font the only way I know is:

Create category UIFont+System and place this code inside

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
    + (UIFont *)systemFontOfSize:(CGFloat)fontSize
    {
        return [UIFont fontWithName:fontName size:fontSize];
    }
#pragma clang diagnostic pop

This will set as well fonts in another places in your app (replacing system font by your font).

Resplendent answered 24/10, 2015 at 14:36 Comment(0)
C
-1

You should go through the UICatalog by Apple. It has all the methods in which a UIDatePicker can be used including the custom date picker method . Study this link developer.apple.com/library/ios/samplecode/UICatalog/ . Also they have implemented a standard switch and the navigation bar which is given in the latter.

Cressy answered 1/6, 2012 at 5:29 Comment(0)
I
-1

A combination of 2 UITableViews is the way to go.

UITableView is a subview of UIScrollView, so it handles the following event

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{

}

and on getting the position, just scroll the table to the center using the following method

[myTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];

Hope this helps.

Ideally answered 9/7, 2012 at 14:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.