UNIQLO's new alarm app has a custom UIDatePicker:
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?
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;
}
Check this out.
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"
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.
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.
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.
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).
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.
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.
Related
When I make a long picker with multiple components, only the components near the center react to the tap-to-select feature.
For example, if you want to pick the very next row under a selected row, you should be able to just tap it to make it animate up into the selection area. This feature still exists with iOS 7, but the area with in the UIPicker that will actually accept the tap is a very small window, no larger than 200px.
Is there any way to expand this tap-able area to include the entire UIPickerView, so any time you tap a row it becomes the selection?
EDIT here is the code, it's pretty standard...
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
NSLog(#"picked %i %i" , component , row);
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 4;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return 15;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return #"";
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
UILabel *pickersLabel;
pickersLabel =[[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, view.frame.size.width , view.frame.size.height )];
pickersLabel.text = [NSString stringWithFormat:#"thing %i" , row ];
pickersLabel.backgroundColor = [UIColor clearColor];
pickersLabel.backgroundColor = [UIColor colorWithRed:1/255.0 green:1/255.0 blue:2/255.0 alpha:0.05];
pickersLabel.opaque = NO;
[view addSubview:pickersLabel];
return pickersLabel;
}
I've had massive problems with UIPickerViews that were in .xibs instead of Storyboards in iOS 7. I'm not sure if there's some optimization going on behind the scenes in the Storyboard or if this is a straight-up bug, but it's worth a shot - I'm using a picker view that's coming from a Storyboard right now and I am able to tap on non-selected rows and they scroll up and become selected.
I want a UIPickerView in following design;
http://dl.dropbox.com/u/53051470/Screen%20shot%202012-04-18%20at%2012.02.36%20PM.png
(sorry as a new user, can't paste image here)
I read other questions on same topic too, and try to implement
pickerView:viewForRow:forComponent:reusingView:
method as well, but din't get the actual results;
Requirements are like selected value should display in green bar with checkmark image, and in white color; Moreover when user will start scrolling the picker, any value that enters inside green bar should turns into green color, and any value that leaves the green bar ares should turns into black;
Any suggestion?
Kind regards;
you should use the method
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
and decide which row is highlight
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
// i use a UILabel instead of your **custom UIView** , you may add a tick in you custom view
UILabel *testRow = view?(UILabel *)view:[[[UILabel alloc] initWithFrame:CGRectMake(0,0, 140, 40)] autorelease];
testRow.font = [UIFont fontWithName:[testArray objectAtIndex:row] size:16];
testRow.text = [fontsArray objectAtIndex:row];
testRow.backgroundColor = [UIColor clearColor];
if ( row == selectedRow )
{
testRow.backgroundColor = [UIColor greenColor];
}
return testRow;
}
don't forget set the showsSelectionIndicator to NO
pickView.showsSelectionIndicator = NO;
UIPickerView multiple-selection-behavior without adding other views in front of the pickerview,using: https://github.com/alexleutgoeb/ALPickerView
Improvements are highly appreciated!
The UISwitch on my device: Switch Image with the bottom pixels cut off http://gorgando.com/uiswitch.jpg
The UISwitch on the simulator: Good UISwitch http://gorgando.com/uiswitch-good.png
As you can see, the bottom pixels are cut off on the device, but not on the simulator. I have tried just about everything I can think of, but nothing has fixed the problem.
Some of the things I've tried:
Changing the UISwitch's frame's height
Changing the UICell's height
Changing the UICell's contentView's height
Adding the UISwitch to the UICell rather than the UICell's contentView
Here is the relevant code:
This is in the viewDidLoad of the uiTableViewController:
UISwitch *sw = [[UISwitch alloc] init];
self.contactedSwitch = sw;
[sw release];
self.contactedSwitch = [UISwitch switchWithLeftText:#"YES" andRight:#"NO"];
self.contactedSwitch.center = CGPointMake(230, 22);
self.contactedSwitch.on = [self.contact.contacted boolValue];
This is where the switchWithLeftText:andRight method comes from:
#import "UISwitch-Extended.h"
#define TAG_OFFSET 900
#implementation UISwitch (tagged)
- (void) spelunkAndTag: (UIView *) aView withCount:(int *) count
{
for (UIView *subview in [aView subviews])
{
if ([subview isKindOfClass:[UILabel class]])
{
*count += 1;
[subview setTag:(TAG_OFFSET + *count)];
}
else
[self spelunkAndTag:subview withCount:count];
}
}
- (UILabel *) label1
{
return (UILabel *) [self viewWithTag:TAG_OFFSET + 1];
}
- (UILabel *) label2
{
return (UILabel *) [self viewWithTag:TAG_OFFSET + 2];
}
+ (UISwitch *) switchWithLeftText: (NSString *) tag1 andRight: (NSString *) tag2
{
UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 94, 27)];
int labelCount = 0;
[switchView spelunkAndTag:switchView withCount:&labelCount];
if (labelCount == 2)
{
[switchView.label1 setText:tag1];
[switchView.label2 setText:tag2];
}
return [switchView autorelease];
}
#end
This is where I add the UISwitch to my tableviewcell:
[[contactedCell contentView] addSubview:self.contactedSwitch];
Thanks so much!
[Update] I thought the tableviewcell's might be the problem, so I added these UISwitches to a regular UIView to see how they looked. I have the exact same problem where they look alright in the simulator and the bottom is chopped in the device. So bizarre!
I had the same problem when programatically creating a UISwitch and changing its frame's origin- it turned out to be a half-pixel issue.
A key to noticing half-pixel bugs is: do the unwanted artifacts appear differently on the simulator vs device?
Your solution of placing the UISwitch with Interface Builder fixes this- you can also make sure to floor() your new coordinates if you are setting the UISwitch's frame's origin x/y.
UISwitch *mySwitch = [[UISwitch alloc] initWithFrame(CGRectZero)];
CGRect switchFrame = mySwitch.frame;
// move frame up without pixel fractions
switchFrame.origin.y = floor((cell.contentView.frame.size.height - switchFrame.size.height) / 2);
mySwitch.frame = switchFrame;
[cell.contentView addSubview:mySwitch];
In the original poster's case, the height of a uiswitch is an odd number (27 pixels), so setting the center to 22 splits the height into 13.5. The UISwitch's origin's y coordinate becomes 22-13.5 = 8.5 pixels. Either don't move a UISwitch by setting its center, or floor the coordinate, or use a fraction in the call to CGPointMake(230,22.5).
Another way to track this type of bug is to grep through Interface Builder looking for ".5" coordinates. I have found that sometimes over-tweaking the placement of UI elements in Interface Builder introduces this bug.
This is caused of the UISwich's top.
You can use UISwich.origin.y = ceil((height - UISwich.size.height)/2)
to fix it.
Never figured out exactly what was wrong, but I ended up creating the UISwitches in IB and got it to work beautifully that way. Very weird that it works in IB and not programmatically when I am basically doing the exact same thing.
How can I change the style of a UIPickerview. I want to change its white background to green color and its black frame to red color.
Anyone please help. I am a beginner.
As far as I know you can't actually change the frame's color, but you can create an image with a transparent area in the middle and add that on top of the frame like so:
[picker addSubview:image];
As for the background color, you can create your own custom views for each row by implementing the UIPickerViewDelegate method:
- (UIView *)pickerView:(UIPickerView *)pickerView
viewForRow:(NSInteger)row
forComponent:(NSInteger)component
reusingView:(UIView *)aView;
You could then set the background color of the view you return via
[myView setBackgroundColor:[UIColor yellowColor]];
However this only affects the rows with data. I don't think you can currently set the background color of the component itself which is a shame.
I personally prefer this:
[self.picker setBackgroundColor:[UIColor greenColor]];
self.picker.layer.borderColor = [UIColor whiteColor].CGColor;
self.picker.layer.borderWidth = 4;
I have a table view with an alphabetical index and am using the side alphabet to get through the list quickly. For those not familiar, that uses this:
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
My problem is that my application has just been skinned black. And so now it's hard to see the alphabet letters on the side.
I can't figure out how to change the color of the alphabet. I'd like it to be 'white' if at all possible.
if your minimum iOS version is newer than 6.0, you can use sectionIndexColor property of UITableView.
The color to use for the table view’s index text.
#property(nonatomic, retain) UIColor *sectionIndexColor
Discussion:
Table views can display an index along the side of the view, making it
easier for users to navigate the contents of the table quickly. This
property specifies the color to use for text displayed in this region.
Update date 2014.1.13
I find an open source third party library:GDIIndexBar to help custom the index appearance.
From what I can tell unfortunately it is not possible to customize the color of the text displayed in the index, the closest I've been able to come is being able to modify the background color and the font of the index.
There is some code in the iPhone Developers cookbook by Erica Sadun which shows how to access the UITableViewIndex view (an undocumented class). You can find the reference to it on page 175 of the book if you have it. Which gives access to the background color and the font. You can see an unofficial document related to this class here.
WARNING This is undocumented use of an undocumented class so you need to be cautious about using it.
Here is a code snippet from the cookbook with minor modifications:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
for(UIView *view in [tv subviews])
{
if([[[view class] description] isEqualToString:#"UITableViewIndex"])
{
[view setBackgroundColor:[UIColor whiteColor]];
[view setFont:[UIFont systemFontOfSize:14]];
}
}
//rest of cellForRow handling...
}
This illustrates how you can access and the UITableViewIndex view and modify it in some aspects. It looks like the view doesn't have any subviews so it is likely doing some custom drawing with the array of index titles.
It's not perfect but hopefully it helps a little.
Paul
This can be easily changed in the interface builder for the UITableView - No need to use undocumented classes?
See screenshot below, as you can see the font colour and the background colour can be changed too. Job's a good'n!
For iOS7 or Higher:
self.tableView.sectionIndexColor = [UIColor whiteColor];
self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];
I was having the same problem and just found out a way of doing this, though this is using undocumented classes and methods so think one extra time before trying to upload it to the App Store.
I should say that i've only tried this with iOS5 so I don't know if it will work for previous versions.
I borrowed and modified the Erica Saunders cookbook example so that it now changes the text color to red:
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
for(UIView *view in [tv subviews])
{
if([[[view class] description] isEqualToString:#"UITableViewIndex"])
{
[view performSelector:#selector(setIndexColor:) withObject:[UIColor redColor]];
}
}
//rest of cellForRow handling...
}
We have successfully used the following code:
for(UIView *view in [tableView subviews]) {
if([view respondsToSelector:#selector(setIndexColor:)]) {
[view performSelector:#selector(setIndexColor:) withObject:[UIColor whiteColor]];
}
}
which works fine - it's very similar to Mooglas answer - but refrains from using the word "UITableViewIndex".
You can set the tint color for the tableView.
[[UITableView appearance] setTintColor:[UIColor purpleColor]];
Swift 5:
tableView.sectionIndexColor = .red
tableView.sectionIndexBackgroundColor = .clear
There is a way to change the color of the index Alphabet.
In your header file, declare your UITableView as a property:
#property (weak, nonatomic) IBOutlet UITableView *mainTable;
Then in your implementation file's viewDidAppear, use the following line:
//Change the color of the UITableView index color
_mainTable.sectionIndexColor = [UIColor blackColor];
Here's the best solution I've found to adjust the background colour of the index bar on the side. It works in iOS7 and probably iOS6.
Add this to your viewDidLoad
if ([_tableView respondsToSelector:#selector(setSectionIndexColor:)]) {
_tableView.sectionIndexBackgroundColor = [UIColor clearColor];
_tableView.sectionIndexTrackingBackgroundColor = [UIColor whiteColor];
}
The first one is the default colour, the second is the background colour when you are scrolling.
Make a mutable array to contain the alternate title label
In
-(NSArray * )sectionIndexTitlesForTableView:(UITableView * )tableView
return an array of #" " where the number of spaces between the quotes determines the width of the hi-lighted scroller.
Have "sectionIndexTitlesForTableView" call an update label function.
In that function remove all the labels in the array you created earlier from their superviews Then create and add however many labels are needed. Then add them to the table's superview.
These are the lines required to place the labels in the right place.
rect.origin.x = table.frame.origin.x+table.frame.size.width-rect.size.width-2;
rect.origin.y = 5+table.frame.origin.y+counter *(280-rect.size.height-([customIndexLabels count]-1))/([customIndexLabels count]-1);
if ([customIndexLabels lastObject]==thisLabel)
{
rect.origin.y-=10;
}
Hope that helps. It's not perfect I just don't care enough to fix it myself
The main problem is that the spacing of the last label is not uniform.
Swift edition for undocumented font change:
extension NSObject {
class func objectClassName() -> String {
let classLongName = reflect(self.self).summary;
let tokens = classLongName.componentsSeparatedByString(".")
if let typeName = tokens.last {
return typeName
}
return NSStringFromClass(self.dynamicType)
}
}
func changeSectionIndexFont(tableView: UITableView) -> Void {
let realSubviews = tableView.subviews as! [UIView]
for subview in realSubviews {
if subview.dynamicType.objectClassName() == "UITableViewIndex" {
subview.setValue(UIFont(name: "OpenSans", size: 10), forKey: "font")
}
}
}