UIPickerView with custom View and labels not scrolling - iphone

I'm using the following code to add a label and a view to a UIPickerView.
- (UIView *)pickerView:(UIPickerView *)pickerView
viewForRow:(NSInteger)row
forComponent:(NSInteger)component
reusingView:(UIView *)view {
CustomPickerView *customView = [[CustomPickerView alloc] initWithFrame:CGRectMake(0.0, 0.0, 180, 32)];
CustomPickerLabel *pickerLabelLeft = [[CustomPickerLabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 80, 32)];
[pickerLabelLeft setTextAlignment:UITextAlignmentRight];
pickerLabelLeft.backgroundColor = [UIColor clearColor];
[pickerLabelLeft setText:#"1234"];
[customView addSubview:pickerLabelLeft];
return customView;
}
The reason I'm using a view is because I want to add two labels to this view and display them in the picker.
The CustomPickerView and CustomPickerLabel classes contain the following code, each:
- (void)didMoveToSuperview
{
if ([[self superview] respondsToSelector:#selector(setShowSelection:)])
{
[[self superview] performSelector:#selector(setShowSelection:) withObject:NO];
}
}
the above code works fine for display and scrolling, but when I click on the label to scroll, it does nothing. If I click just outside the label, as in the corners of the picker, the wheel turns to the selection as it should.
Any suggestions would be appreciated.
Rod

Set your customView's userInteractionEnabled property to NO. It seems that if it is set to YES then custom view intercept touches and picker can't scroll to the tapped row.

Related

Customizing UIPickerView (background and spacing)

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];
}
EDIT 1
For RickiG (on background):
EDIT 2
For RickiG:
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;)
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.
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.
How about adding an empty view on the right of your labels? And for the background you should probably use insertSubview:aboveView: method with the default background view as the second parameter (if you can access to it).

Is it possible to provide a UIView to a UIPickerView that 'fills' the row?

I am trying to provide custom views to a UIPickerView so that the entire background of each row is one of two colours (none of which are white).
I have been able to provide these custom views ok, with the colours I want, but I can't make the view fill the entire 'space' for the row.
The test code below is trying to achieve the effect of changing the background colour of the UIPicker to green - but the view I provide does not get aligned correctly. It is always a few pixels to the left and sometimes actually covers the left edge of the component depending on its width.
The effect of this is that I always see a white edge on the right hand side.
Changing the origin of the CGRect does not change its location in the row.
Changing the width of the CGRect changes the width ok but it is still always aligned a few pixels towards the left. i.e. not centered.
Can anyone please tell me what am I doing wrong here?
I just have a standard UIPicker dropped on to a view.
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return 44;
}
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
return 200;
}
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UIView *rowView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 44)] autorelease];
rowView.backgroundColor = [UIColor greenColor];
return rowView;
}
Ok, I have managed to at least 'center' the view.
I had to add a subview to the view that is returned from viewForRow and try a few values for the origin.x value like so:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UIView *rowView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 44)] autorelease];
UIView *subView = [[[UIView alloc] initWithFrame:CGRectMake(6, 0, 194, 44)] autorelease];
subView.backgroundColor = [UIColor greenColor];
[rowView addSubview:subView];
return rowView;
}
For some reason though I have not been able to get the view to extend to the very edges of the row so the white edges that appear either side of the view remain. I can live with this.
Hope this helps someone else.
- (UIView *)pickerView:(UIPickerView *)apickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UIView *rowView = nil;
if(view==nil) {
rowView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)] autorelease];
rowView.backgroundColor = [UIColor greenColor];
return rowView;
}
return view;
}
the "trick" is in returning the reusingView if it is available.
After this it's up to you to configure the view as you wish.
The "320" value is just here to test you should instead put some computed value such as the pickerView.frame.size.width so the code would be cleaner.

UIPickerView tap to scroll & custom row UIViews

You can scroll an UIPickerView both by swiping and by tapping items under or above the selection belt.
You can populate an UIPickerView by either specifying NSString as titles or reusable UIViews for each row.
What I've noticed is that when I switched from providing strings to providing view, tap to scroll no longer worked. Is this expected behavior. Should my views forward some touch events to the UIPickerView?
Thanks
EDIT:
Here's a code sample from my implementation:
// Creating the picker
screenPicker_ = [UIPickerView new];
screenPicker_.showsSelectionIndicator = YES;
screenPicker_.delegate = delegate_;
screenPicker_.dataSource = delegate_;
[self addSubview:screenPicker_];
[screenPicker_ release];
// Row view creation delegate
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
PickerRowView *pickerRowView = (PickerRowView*)view;
if(view == nil)
{
pickerRowView = [[PickerRowView alloc] initWithFrame:CGRectMake(0, 0, pickerView.frame.size.width, PICKER_ROW_HEIGHT)];
}
[pickerRowView SetTitle:#"some title"];
return pickerRowView;
}
// initailizer of the PickerRowView class (an UIView subclass)
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
CGFloat titleHeight = frame.size.height * CONTENT_TO_FRAME_RATIO;
title_ = [[UILabel alloc] initWithFrame:CGRectMake(TITLE_X, (frame.size.height - titleHeight) / 2, frame.size.width, titleHeight)];
[title_ setFont:[UIFont fontWithName:#"StainlessExt-Light" size:titleHeight]];
title_.backgroundColor = [UIColor clearColor];
[self addSubview:title_];
[title_ release];
self.userInteractionEnabled = NO;
}
return self;
}
Setting userInteractionEnabled property to NO for your row views must solve your problem.
In your PickerRowView class, define the following method:
- (void)didMoveToSuperview {
if ([[self superview] respondsToSelector:#selector(setShowSelection:)])
{
[[self superview] performSelector:#selector(setShowSelection:) withObject:NO];
} }
and that should get rid of the highlight

right align text in UIPickerView

How can I right align text in a UIPickerView? I tried to make custom UILabels for the row views, but for some reason, nothing is showing up, right-aligned or otherwise. Here's what I wrote:
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:[NSString stringWithFormat:#"row %d", row]];
[label setTextAlignment:UITextAlignmentRight];
return [label autorelease];
}
In case anyone is wondering, I used CGRectZero because I saw it in the UICatalog example.
I have taken two components in picker and two arrays to set the title of the rows in a particular component.
Below code will display pickerdata in center with default font and fontsize of the picker.
It will give exact pickerdata display behavior with center alignment of the pickerdata.
Here,
NSArray *component1Array=[NSArray arrayWithObjects:#"0 lbs",#"1 lbs",#"2 lbs",#"3 lbs",#"4 lbs",#"5 lbs",nil];
NSArray *component2Array=[NSArray arrayWithObjects:#"0.00 oz",#"0.25 oz",#"0.50 oz",#"0.75 oz",#"1.00 oz",nil];
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
//I have taken two components thats why I have set frame of my "label" accordingly. you can set the frame of the label depends on number of components you have...
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 145, 45)];
//For right alignment of text,You can set the UITextAlignmentRight of the label.
//No need to set alignment to UITextAlignmentLeft because it is defaulted to picker data display behavior.
[label setTextAlignment:UITextAlignmentCenter];
label.opaque=NO;
label.backgroundColor=[UIColor clearColor];
label.textColor = [UIColor blackColor];
UIFont *font = [UIFont boldSystemFontOfSize:20];
label.font = font;
if(component == 0)
{
[label setText:[NSString stringWithFormat:#"%#",[component1Array objectAtIndex:row]]];
}
else if(component == 1)
{
[label setText:[NSString stringWithFormat:#"%#", [component2Array objectAtIndex:row]]];
}
return [label autorelease];
}
You should comment below mention UIPickerView delegate method if you are using above method...
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
The output of above sample code will look like below
Your not seeing anything because of CGRectZero. You need to set a size in your case.
In the UICatalog, if your talking about how they used CGRectZero for the CustomView... well if you look at CustomView.m you will see they are actually disregarding the CGRectZero and setting a frame to a size in the initWithFrame:
In iOS 6, you can now return an NSAttributedString, which can contain text alignment attributes. I posted a short snippet on another related question here: https://stackoverflow.com/a/14035356/928963
Make sure you:
Provide an explicit frame that is as high and wide as needed;
Set the label's display opaqueness (via .opaque = YES or NO) and background color appropriately (you usually want NO and [UIColor clearColor] respectively).
Set the font explicitly.

UIPicker sizing in landscape mode

I am trying to develop an app with a UIPicker in landscape mode, taking up (almost) the entire width of the screen (with 5 or 6 components). Can you please tell me how to set the size of UIPicker. Thank you very much for your help.
Actually, I resize my pickers for almost every app. I do not like that they take up the entire screen. Here is the method that I am using: (note that I am also rotating the picker to be horizontal)
in viewDidLoad .....
picker = [[UIPickerView alloc] initWithFrame:CGRectZero];
picker.delegate = self;
picker.dataSource = self;
picker.showsSelectionIndicator = NO;
//Resize the picker, rotate it so that it is horizontal and set its position
CGAffineTransform rotate = CGAffineTransformMakeRotation(-1.57);
rotate = CGAffineTransformScale(rotate, .46, 2.25);
CGAffineTransform t0 = CGAffineTransformMakeTranslation(3, 22.5);
picker.transform = CGAffineTransformConcat(rotate,t0);
[self.view addSubview:picker];
[picker release];
Then, I like to add a background image for my view that covers up the grey bars (which are now on the top and bottom) of the UIPicker:
//Create the overlay to superimpose ontop of the picker
//In this case, the image is the size of the screen with a transparent area the size of the //UIPickerView cut out in the middle
UIImageView *uiiv = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"problem_bg.png"]];
uiiv.userInteractionEnabled = NO;
uiiv.opaque = YES; //for performance
[self.view addSubview:uiiv];
[uiiv release];
UIPickerView delegate method:
-(UIView *) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)rowforComponent:(NSInteger)component reusingView:(UIView *)view
{
UIView *viewForRow = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 280)] autorelease];
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"myimage.png"]];
img.frame = CGRectMake(0, 0, 102,280);
img.opaque = YES;
[viewForRow addSubview:img];
[img release];
UILabel *label;
UIFont *font = [ UIFont fontWithName:#"Helvetica" size:20];
label = [[[UILabel alloc] initWithFrame:CGRectMake(10, 20, 270, 100)] autorelease];
label.text = #"I am a label";
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor blackColor];
label.font = font;
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[viewForRow addSubview:label];
CGAffineTransform rotate = CGAffineTransformMakeRotation(1.57);
[viewForRow setTransform:rotate];
return viewForRow;
}
This gives a much smaller, horizontal picker with a nice look and feel. I hope this helps someone.
You can edit the size by opening the .xib file in TextEdit and changing the size of the UIPickerView.
You can't. UIPickerView's size is constant.
UPDATE: It turns out you can resize an UIPickerView. The trick is to place it inside another (smaller) UIView, and resize that view. I haven't tried this yet.
UPDATE 2: This method does not resize the UIPickerView, but rather crops it. It might or might not be what you're looking for, but AFAIK, there's no way to truly resize an UIPickerView, and this is as close as it gets. It doesn't look that bad.
UPDATE 3 (Long overdue): As of SDK 3.0, UIPickerView is completely resizeable using initWithFrame or setFrame.
I wrestled with the same issue of resizing the pickerview. After some research and headache here is something you can easily do:
Forget Interface builder! I think you can so much better UI without
the interface builder.
In your controllers overwrite the load view method.
-(void)loadView
{
//create your view
self.view = [[UIView alloc] initWithFrame:CGRectZero];
// create a default sized pickerView
pickerView = [[UIPickerView alloc] initWithFrame:CGRectZero];
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
// Get its frame
CGRect pickerFrame = pickerView.frame;
// Set it to what ever you like, I use a default screen height times a shrink factor like 0.75 for 3/4 of its original size
pickerFrame.size.width = screenWidth*pickerShrinkFactor;
pickerFrame.size.height = pickerHeight*pickerShrinkFactor;
// You can also set the upper left corner of the picker
pickerFrame.origin.x = 0;
// Set the picker frame to new size and origin
pickerView.frame = pickerFrame;
// Add it to your view
[self.view addSubview:pickerView];
}
Good luck
I'm fairly certain that the UIPicker comes in one size, which you can't change. Would be interested to hear different.
The answer is in:
-(UIView *) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
Since with:
- (NSString*)pickerView:(UIPickerView*)pv titleForRow:(NSInteger)row forComponent:(NSInteger)component
Makes it work (that is: sideways with scaled label) of course
No problem (I just registered). By the way I just realized that when I posted my answer, that the method (UIView *) pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent .... lost the space between row and forComponent. So make sure that you have the delegate correct or else it will not work.