iOS development: Customizing UIPickerView & UIDatePicker

The need for custom controls

If you ever implemented an iOS app customizing standard control elements, you may know how hard it may be at times to get a standard control look like that masterpiece your designer drew out meticulously. We do a lot of custom-designed apps here at Inexika, and we'd love to share some code & tricks that you would hopefully take advantage of. We'll publish several case studies in a series of small blog posts, and this is the first one.

Here we will show how to customize a background texture, selection indicator (glass), and even the subtlest aspects of how selection wheels look in UIPickerView & UIDatePicker controls.

Customization strategies

We have first come to changing the appearance of UIPickerView while working on a custom-designed app for one of our clients. We needed a picker that would have custom background texture and selection glass.

custom UIPickerView in one of the apps we developed

As there were no dynamic elements that would change at runtime, we chose the simplest way to do the job — by overlaying the picker with an image that contained a frame and the glass. This approach was good for that situation as all we had to do was prepare the image and drop it upon the picker in xib file. If you have a graphics designer at hand you should be happy with this approach up until the moment you realize you need a custom look for a picker that may change its appearance (in particular, the number and dimensions of selection wheels) at runtime, such as UIDatePicker which changes depending on your current date/time format. In our new product iMood Diary, our senior iOS developer Vadim implemented a clever picker overlay view that enables customization of dynamically defined UIPickerView objects & UIDatePicker (the latter does not inherit from UIPickerView but instead contains it as a subview). See how our IXPickerOverlayView transforms the look of the control:

  • before:

UIDatePicker

  • after:

UIDatePicker with IXPickerOverlayView

Overall appearance of the picker can be changed with just 2 graphic assets:

  • the background texture image (can be tiled!), and
  • the selection indicator image (usually glass).

You can also change the tint of the wheels and bevels, but that's a bit tricky and not as flexible as you might expect: you or your designer will need to know exactly how view/layer compositing works in order to prepare proper graphical assets.

How it works

IXPickerOverlayView is actually dead simple. You provide it with a reference to a picker view, and it polls picker for the parameters of its wheels (components), then masks out the region corresponding to the wheels using CAShapeLayer. All kinds of overlays are then applied. If IXPickerOverlayView cannot find a UIPickerView instance in its host's hierarchy (just in case Apple changes the implementation of UIDatePicker), it falls back to presenting nothing so that the standard picker view appears in the UI.

IXPickerOverlayView automatically changes with UIDatePicker when locale settings change. If you use custom-configured picker, you need to call setNeedsLayout when you change the number or width of selection wheels — the overlay will catch up.

How to use it?

Hey, it's open source and available under MIT license! Download the latest version of the source code from GitHub. You will find a sample project in the package. To integrate IXPickerOverlayView into your project:

  1. Drag IXPickerOverlayView folder to your project navigator in Xcode (be sure to copy the resources)
  2. Link your current target with QuartzCore framework
  3. Place IXPickerOverlayView instance over your picker view with the same frame as the picker view
  4. Set IXPickerOverlayView's hostPickerView outlet (be sure not to add the overlay to picker as a subview: this will lead to reference cycle as the outlet is a strong reference)
  5. If you use a custom-configured picker, be sure to call setNeedsLayout on corresponding IXPickerOverlayView instance once you change the number or width of selection wheels.
  6. Replace ixPickerOverlayTexture & ixPickerOverlayGlass images to give the control its desired appearance.

That's it!

More nice & tidy pieces of a code to come. Stay tuned!

Comments

Hi, A quick clarification on step 3 of UIPickerView post..

How/Where do I find/create an instance of the IXPickerOverlayView to be dragged and placed over my pickerView?

Thanks in advance.

Hi, I need help please. I have problems on step 3, 4 and 5.
In my project i have a UIView with a UIScrollView inside, and inside the ScrollView there is my picker view. What should I do to customize the picker? I have not understood very well. Can you help me please with the configuration? What I have to connect to the hostPickerView outlet? The View or the PickerView?
Finally, I did not understand what is the function setNeedsLayout and how I have to call it.
Thanks in advance.

Hi MetalSlug,

Please take a look at the sample project. You will find answers to all your configuration questions there.
You only need to call setNeedsLayout on the IXPIckerOverlayView object when you adjust host picker view's appearance (change number or width of picker view components).

Leonty Deriglazov

Hi Leonty Deriglazov,
I downloaded all the zip file but when I open the file PickerOverlaySample.xcodeproj many files are missing in the project (the name of the missing files is red).
Could you check please?
Thanks in advance.

Hi Leonty Deriglazov,
could you please post an example with also a custom UIPickerView? Because in the sample available for download there are two UIDatePicker and no one PickerView.
I would appreciate a lot your help for that.
Thanks in advance.

There would be no difference at all unless you want to change the appearance of the picker (number or width of the components) dynamically, in which case you would just call [pickerOverlay setNeedsLayout]; upon that change (you would see if this did not work ;). Thanks for your attention to our shared code. We appreciate it. Let me know if you continue to have any problems incorporating IXPickerOverlayView.

Leonty Deriglazov

Hi Leonty Deriglazov,
your code is very useful! Great job! But I don't understand where should i call the setNeedsLayout metod. I explain you what I did, please could you tell me where wrong?
I created a view and I set its class to IXPickerOverlayView and I put it on my pickerView. After i connected the IXPickerOverlayView's hostPickerView outlet to my pickerView.
Then I created @property (nonatomic, retain) IBOutlet UIView *PickerOverlay and I connected it to the IXPickerOverlayView (the view on the picker).
The problem is now. Where should I call [pickerOverlay setNeedsLayout] ? Maybe in viewDidLoad, or in viewDidAppear, or in pickerView methods (numberOfRowsInComponent, or in titleForRow, or in didSelectRow) ???

Please help me :(
Thanks in advance.

It works, I have the brown picker, but it's empty!! The objects's name that I inserted are not displayed :( The background is white without names :(

Thanks in advance.

It is likely that you create an instance of IXPickerOverlayView with a white backgroundColor, which is a default value for UIView.
Try setting "backgroundColor = [UIColor clearColor]".
Also, set "opaque = NO" and "userInteractionEnabled = NO".
If you have IXPickerOverlayView in xib, you can set all those properties there (or just copy-paste the view from the example project).
We're going to address this issue soon by forcing those properties to IXPickerOverlayView instances automatically.

Now all works!!! :) the problem was the "backgroundColor = [UIColor clearColor]". ! If I ever meet you I will offer to you a coffee! :) You were very kind. Thanks for everything. Bye bye.

The example code works, but I was playing around a bit with the code - as you wrote in a previous code: "You only need to call setNeedsLayout on the IXPIckerOverlayView object when you adjust host picker view's appearance (change number or width of picker view components)"

I added files ViewController.h/.m so in AppDelegate.m i can just call self.viewController = [[ViewController alloc] init];
Then I tested by adding this code:

-(void) testing
{
[datePicker setDatePickerMode:UIDatePickerModeDateAndTime];
}

- (void)viewDidLoad
{
[super viewDidLoad];
[self performSelector:@selector(testing) withObject:nil afterDelay:3];
}

So assuming the date picker was in another initial mode (only date in my case), the layout will have to be redrawn, but I cannot get it to work.

If I change the test code to
[datePicker setDatePickerMode:UIDatePickerModeDateAndTime];
[overlay1 layoutSubviews]; // overlay1's hostPickerView = datePicker,
it will work correctly, but I thought the idea with registering with the NSNotificationCenter is that it should be updated automatically, is that correct? But I cannot get that to work.

Hi Leonty

Now downloaded and installed your demo software application and it ran very well. It's a great credit to you for developing such a great piece of software, perhaps the only way to style the PickerView control.

Having trouble with step No 3 above

Now I created an instance of IXPickerOverlayView class as you documented above.

Now when I select my view, I click on the identity Inspector in interface builder and click on the (custom class) class drop down combo. Now there are a lot of classes available to me here including the IXPickerOverlayView class. Now when I click on my PickerView control and again as I did for my view and select the (custom class) class drop down combo, there is only one class available to me UIPickerView. My question is how do I make available the XIPickerQverlayView class to my PickerView control as you have done in your demo application. Currently working with Xcode 4.5.1.

Any help much appreciated

Kind Regards

David Egan

Hi David!
Thanks for you response.

Please read step 3 again:
3. Place IXPickerOverlayView instance over your picker view with the same frame as the picker view.

IXPickerOverlayView and UIPickerView are two different classes and their instances should be placed on view controller at the same time. IXPickerOverlayView is only overlay on UIPickerView.
As you can see in example project (please open ViewController.xib in Interface builder) we have two instances of UIPickerView and two instances of IXPickerOverlayView (over each PickerView). Every instance IXPickerOverlayView have outlet to pickerView via hostPickerView.
So you don't need make available the XIPickerQverlayView class to PickerView. You just need use UIPickerView as usual.

Best Regards
Vadim

Hi Vadim

After looking at step three above. I am lost Place IXPickerOverlayView instance over your picker view with the same frame as the picker view.

THE SAME FRAME AS THE PICKER VIEW. What exactly do you mean be by FRAME as the picker view.

Any help much appreciated

Kind Regards

David Egan

The frame (CGRect) is the position and size of view rectangle in the superview.
THE SAME FRAME AS THE PICKER VIEW - it means that the position and size of IXPickerOverlayView instance should be the same as the position and size of picker view.

Best Regards
Vadim

Hi Vadim

I am still missing something here. When I examine you demo. I click on ViewController.xib file containing your picker view controls. Now when I click on either of the picker view controls and click the identity inspector tab. In the custom class section I click on the class drop down and I see the IXPickerOverlayView class is available there.

Now when I repeat the same steps for my own project the only class available to me is the UIPickerView class There is no reference to the IXPickerOverlayView Class.

So there is something fundamentally I am missing here? So how do i get the IXPickerOverlayView class to appear in the class area of my PickerView? Do I need to drag the class from one area of xcode to another. Please explain

Kind Regards

David Egan

David, you need add two different views in your view controller - UIPickerView and UIView(!) which you change the class to IXPickerOverlayView.

When you click on picker view in our demo you just select IXPickerOverlayView instance and not UIPickerView. Try to click and move view and you will see that there two different views: one of them is our control (IXPickerOverlayView) and one is UIPickerView.

Hi Vadim

Yes I cracked It, your last post was very helpful. I would suggest providing a graphical tutorial, I am sure it would help a lot of people.
I found that perhaps the text dialogue above difficult to follow.

Thank's for your assistance

Kind Regards

David Egan