Customizing UIPickerView (background and spacing)

13,184

Solution 1

Hi There is no direct way to change the background. What you can do is to have the view you return in viewForRow feature its own background (then add the shadow in each side afterwards if you need it). You can also go looking for viewWithTag: but that is never a good idea as this might change in future iOS releases.

Is there a special reason you implement both viewForRow and TitleForRow? I usually just populate the viewForRow's labels inside this delegate method.

The viewForRow has the ability to reuse the views in the Picker, much like a UITableView you should test if the "reusingView:(UIView *)view" is nil and if not there is no need to draw everything again. Just populate the labels.

I usually never customize the picker, if I need something not completely custom I subclass the UITableView, it is much more flexible and can what the Picker does + more.

For the spacing you can use the "height" of the rows, the picker will center the views you return in viewForRow, then just make sure:

- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component

returns a value bigger than your view.

Held og lykke;)

Solution 2

I just figured out how to apply background color or image in the picker view. Hope it may help someone.

Just define a category in the file where you want to you picker view as follow:

@implementation UIPickerView(Extension)

-(void)drawRect:(CGRect)rect
{

    NSArray* subviews = [self subviews];
    for(UIView* view in subviews)
    {
        if([view isKindOfClass:[UITableView class]])
        {
            view.backgroundColor = appColor;
        }
    }
    [super drawRect:rect];
}

@end

You can look further at subviews for more customization.

Solution 3

Looks like this is an old thread, but in iOS 7 (Possibly earlier, I'm not sure), UIPickerViews have a background color property, so setting the background color is simple:

[pickerView setBackgroundColor:[UIColor whiteColor]];

Setting a background image is similarly simple thanks to [UIColor colorWithPatternImage:]. (Note that the pattern will tile if the image is smaller than the UIPickerView.)

[pickerView setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"someBackgroundImage.png"]]];

To modify the component widths you can implement the delegate method widthForComponent in order to set various widths for each component. In theory, you should be able to add an empty component and use it's blank width to create spacing, but I haven't tried this.

- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;

I haven't been able to find a way to change the padding between components which seems like a nicer way to modify the spacing and would also allows you to remove the ~5 px spacing between components, but if I'm able to find one I will update my answer.

Share:
13,184
simonbs
Author by

simonbs

Updated on June 23, 2022

Comments

  • simonbs
    simonbs about 2 years

    I would like to change the white background in a UIPickerView to an image of my own.

    Is this possible?

    Also, I have managed to get my UIPickerView to scroll horizontally instead of vertical. Now, I would like to know if there is any way to adjust the spacing between two rows of the picker view?

    I have attached an image to show what I mean.

    This is my code:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
    
        arrayDays = [[NSMutableArray alloc] init];
        [arrayDays addObject:@"ONSDAG"];
        [arrayDays addObject:@"TORSDAG"];
        [arrayDays addObject:@"FREDAG"];
        [arrayDays addObject:@"LØRDAG"];
    
        arrayDates = [[NSMutableArray alloc] init];
        [arrayDates addObject:@"29. JUNI"];
        [arrayDates addObject:@"30. JUNI"];
        [arrayDates addObject:@"1. JULI"];
        [arrayDates addObject:@"2. JULI"];
    
        pickerViewDay = [[UIPickerView alloc] initWithFrame:CGRectZero];
        [pickerViewDay setDelegate:self];
        [pickerViewDay setShowsSelectionIndicator:NO];
        CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI/2);
        rotate = CGAffineTransformScale(rotate, 0.25, 2.0);
        [pickerViewDay setTransform:rotate];
        [pickerViewDay setCenter:CGPointMake(self.view.frame.size.width/2, (pickerViewDay.frame.size.height/2)-3)];
        [self.view addSubview:pickerViewDay];
    
        // Adding selection indicator to pickerview
        UIImage *selectorImage = [UIImage imageNamed:@"DayPickerView_SelectionIndicator.png"];
        UIView *customSelector = [[UIImageView alloc] initWithImage:selectorImage];
        [customSelector setFrame:CGRectMake(0, 0, 120, 74)];
        [customSelector setCenter:CGPointMake(self.view.frame.size.width/2, customSelector.frame.size.height/2)];
        [self.view addSubview:customSelector];
        [customSelector release];
    
        // Adding background to pickerview
        UIImage *backgroundImage = [UIImage imageNamed:@"DayPickerView_Background.png"];
        UIView *custombackground = [[UIImageView alloc] initWithImage:backgroundImage];
        [custombackground setFrame:CGRectMake(0, 0, 320, 74)];
        // [self.view addSubview:custombackground];
        [custombackground release];
    }
    
    - (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
        UIView *viewRow = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 150, 80)];
    
        CGAffineTransform rotate = CGAffineTransformMakeRotation(3.14/2);
        rotate = CGAffineTransformScale(rotate, 0.25, 2.0);
    
        // Date
        CGRect rectDate = CGRectMake(30, 0, 150, 80);
        UILabel *date = [[UILabel alloc]initWithFrame:rectDate];
        [date setTransform:rotate];
        [date setText:[arrayDates objectAtIndex:row]];
        [date setFont:[UIFont fontWithName:@"Arial-BoldMT" size:37.0]];
        [date setShadowColor:[UIColor whiteColor]];
        [date setShadowOffset:CGSizeMake(0, -1)];
        [date setTextAlignment:UITextAlignmentCenter];
        [date setBackgroundColor:[UIColor clearColor]];
        [date setClipsToBounds:YES];
        [viewRow addSubview:date];
    
        // Day
        CGRect rectDay = CGRectMake(-30, 0, 150, 80);
        UILabel *day = [[UILabel alloc]initWithFrame:rectDay];
        [day setTransform:rotate];
        [day setText:[arrayDays objectAtIndex:row]];
        [day setFont:[UIFont fontWithName:@"Arial-BoldMT" size:21.0]];
        [day setTextColor:[UIColor colorWithRed:0.35 green:0.35 blue:0.35 alpha:1]];
        [day setTextAlignment:UITextAlignmentCenter];
        [day setBackgroundColor:[UIColor clearColor]];
        [day setClipsToBounds:YES];
        [viewRow addSubview:day];
    
        return viewRow;
    }
    
    - (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
        return [arrayDays objectAtIndex:row];
    }
    
    - (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
        return [arrayDays count];
    }
    

    Example

    EDIT 1

    For RickiG (on background):

    @RickiG: Backgrounds are messed up and there are gaps

    EDIT 2

    For RickiG:

    @RickiG: Gaps in each side of the pickerview

  • simonbs
    simonbs about 13 years
    Thank you very much for the tip on spacing. That's perfect! I can't seem to set the background the way that you mention, as there will be a gap between the views of the backgrounds. I believe this is due to the spacing. Also the background looks strange (it has two "shadows", it's supposed to only have one in the top) Check my first post.
  • simonbs
    simonbs about 13 years
    This is a bit of a risky approach, it seems. As the views are stacking and until objects are added to the UIPickerView, the number of subviews is zero. If you have a more exact way in your mind, please let me know. I can't seem to get this working correctly.
  • RickiG
    RickiG about 13 years
    Well if your background image does not fit the size of the component there will be a gap. You can either make the background the same size as the component or maybe use stretchableImage to make the UIImage fit the component.
  • simonbs
    simonbs about 13 years
    You were right. I played around width the width of the labels, the view and the image and not the background for the views are fine. I have a gap in each side of the pickerview, though. Do you know if there is any way to set a background there? For the shadow effect, I could just put an image on top of the pickerview. That would be perfect but I would still need the background itself. Check my first post for an image.
  • RickiG
    RickiG about 13 years
    This is why I suggested the UITableView, the pickerView is a nightmare to customize. I guess you have to pad your data with an empty component in each end, then check if the component is selected and if it is do [pickerView selectRow:row-1 inComponent:0 animated:YES] for the when the last component is selected and selectedRow:row+1 for the first component. If the user selects the first or last component (the padding) the picker will spring back, or forth, to a valid component. The alternative of digging out the background is dangerous and sensitive to iOS changes, which happens a lot.