iOS6 UIPickerView memory leak issue. - iphone

I was getting this memory leak:
[UIPickerTableViewTitleCell initWithStyle:resuableIdentifier];
and
NSConcentrateMutableAttributedString.
Issue was that I had not implemented this delegate. After implementing this now memory leaks goes away. May be this information helpful for other as I spend mine 16 hours only to figure out this issue.
// Do something with the selected row.
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
// Get the text of the row.
NSString *rowItem = [NSString stringWithFormat:#" %#",[machineData objectAtIndex:row]];
// Create and init a new UILabel.
// We must set our label's width equal to our picker's width.
// We'll give the default height in each row.
UILabel *lblRow = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, [pickerView bounds].size.width, 44.0f)];
// Make the text color red.
[lblRow setTextColor: [UIColor blackColor]];
[lblRow setFont:[UIFont boldSystemFontOfSize:20]];
// Center the text.
[lblRow setTextAlignment:UITextAlignmentLeft];
// Add the text.
[lblRow setText:rowItem];
// Clear the background color to avoid problems with the display.
[lblRow setBackgroundColor:[UIColor clearColor]];
// Return the label.
return lblRow;
}

Thanks for your info. Was confused by this leak. Only few comments:
probably lblRow should be autoreleased: return [lblRow autorelease];
[pickerView rowSizeForComponent:component] can be used to get size for new label.

I used IUIPicker in a popover and every time I dismissed the popover I had memory leak. I am also using ARC, so the easiest way I resolved this was by setting the UIPickerView = nil on unload. The following appears to have done the trick.
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.pickerView = nil;
}

Related

Memory leak when scrolling UIScrollView

In my iPhone app, I am dynamically adding a UIScrollView and adding n number of UIImages and UIButtons into the scrollview. Here, the images are loaded from different urls and the button titles are coming from SQlite database. Everything is fine. But when I scroll the scrollview, now I am getting memory warning Level=1 and after some time it is Level=2 and crashes the app. I am using ARC. How can I fix this problem?
Code
- (void)setUpViewLayout{
int newContentSize = [appDelegate.itemArray count] * 125;
menuItemIdArray = [[NSMutableArray alloc]init];
mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
mainView.contentSize = CGSizeMake(newContentSize, 220);
mainView.tag = 100;
mainView.delegate = self;
mainView.userInteractionEnabled = YES;
mainView.backgroundColor = [UIColor clearColor];
int xPosition = 20;
for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
[self createImage:xPosition];
[self createButton];
xPosition = xPosition + 120;
}
[self.view addSubview:mainView];
}
- (void)createImage:(int)xPosition{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
NSString *url = [NSString stringWithFormat:#"%#",itemObj.notAvialableIcon];
imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
imgView.userInteractionEnabled = YES;
imgView.tag = tagVal;
[imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:#"item01.png"]];
[mainView addSubview:imgView];
}
- (void)createButton{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(5, 90, 100, 26);
button.tag = tagVal;
button.userInteractionEnabled = YES;
button.tag = tagVal;
button.titleLabel.textColor = [UIColor blueColor];
button.titleLabel.font = [UIFont systemFontOfSize:9.0];
NSString *name = [NSString stringWithFormat:#"%#",itemObj.itemStatus];
itmName = [NSString stringWithFormat:#"%#",itemObj.itemName];
NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];
[button setTitle:date forState:UIControlStateNormal];
button.userInteractionEnabled = NO;
button setBackgroundImage:[UIImage imageNamed:#"not_available_bttn_bck_img"] forState:UIControlStateNormal];
[imgView addSubview:button];
}
Don't add all subviews to the scroll view at one time. That's too expensive.
When scroll view did scroll, get the visible rect of the scroll view, and add your image and button just fit in than range or more than a little of that rect.
When the visible subview is not visible, remove from the super view.
I'm opening a new response for this one. It's the simple solution we all missed.
From the code you posted, this is just a table view on its side. So, you don't have to build your own tiled scroll view.
Here's a bit of code to get you started. When you set up the table view, rotate it by 90 degrees, set the row height and eliminate the separator lines:
tableView.transform = CGAffineTransformMakeRotation(0.5 * M_PI);
tableView.rowHeight = 120.0;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
You'll have to set the table view's frame so that it's in the correct position after rotation. Essentially, it's the same as your current scroll view's frame, or as that frame on its side.
Here are a couple of the table view's data source methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [appDelegate.itemArray count];
}
The table cells can be very simple custom table cells that just have a single image view and a button on that image view. You might also rotate the image view so that the image is displayed correctly, not on its side. Or, you could rotate all your images in a photo editor or image editor before loading them.
That's pretty much it. The table view, as always, will take care of recycling your cells and optimizing your memory usage.
You need to identify which part of your code causes the leak. You can do this several ways.
One way is to use the built in analyzer in xcode. It analyzes your code and detect (some) potential memory leaks.
The instruments tool is also a good tool to find these leaks. Start it using the allocation/leak component. Go to your scrollview, and do a sample after scrolling the view. Your leak should show up. Now you can track down the leak and have instruments locate the correct place in your code directly.
The third option is to go through your code and try and figure out what is happening yourself. Understanding memory managment is a vital part of programming for ios devices.
What about posting the code your are using in your scrollview here, so we can take a look?
Abhishek is absolutely correct that all subviews must be released after being added to a superview. That will cause a leak. Specifically, once the scroll view comes off screen and is released, its subviews will not be released as they should. They will still have a retain count of 1, from when they were alloc'ed.
However, as long as the scroll view is still on screen, there is no leak. A superview retains all its subviews (i.e. increases their retain count by 1.) If a subview was alloc'ed but not released, it's retain count is 2. If it was alloc'ed and released its retain count is 1. Either way, as long as the scroll view exists, its subviews are still, correctly, retained.
If you are receiving memory warnings while the scroll view is still up, the problem may not be the leak, just over-usage of memory. If you keep adding images to a large scroll view, you will certainly run into memory overage problems.
To fill a large scroll view with images, but avoid memory overages, you might take a look at the ScrollViewSuite demo's third example, on tiling. That should work well for you since your images and buttons are the same size, and can act as the tiles.
The idea is to make a sort of table view out of the scroll view that now recycles image tiles instead of cells. The scroll view is subclassed and a set of reusable tiles is kept as one of its instance variables. The key to the implementation is, in layoutSubviews, to remove from superview the tiles that have moved out of the visible area, then recycle tiles for newly visible content and add them as subview. In this way, only visible tiles are loaded into memory. And, it recycles tiles just like a table view recycles cells.
From the size of your scroll view, it may be that you have no other option than to tile and recycle. Nonetheless, it's a good option.
Update: Wubao Li essentially summarizes what needs to be done. The ScrollViewSuite demo shows you how.
//you had allocated the things but did not release it ... it was the reason of leak
- (void)setUpViewLayout{
int newContentSize = [appDelegate.itemArray count] * 125;
// menuItemIdArray = [[NSMutableArray alloc]init]; why you are allocating this array
UIScrollView *mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
mainView.contentSize = CGSizeMake(newContentSize, 220);
mainView.tag = 100;
mainView.delegate = self;
mainView.userInteractionEnabled = YES;
mainView.backgroundColor = [UIColor clearColor];
int xPosition = 20;
for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
[self createImage:xPosition];
[self createButton];
xPosition = xPosition + 120;
}
[self.view addSubview:mainView];
[mainView relese];//release scroll view here
}
- (void)createImage:(int)xPosition{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
NSString *url = [NSString stringWithFormat:#"%#",itemObj.notAvialableIcon];
imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
imgView.userInteractionEnabled = YES;
imgView.tag = tagVal;
[imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:#"item01.png"]];
[mainView addSubview:imgView];
[imgView release]; //release imageview here
}
- (void)createButton{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(5, 90, 100, 26);
button.tag = tagVal;
button.userInteractionEnabled = YES;
button.tag = tagVal;
button.titleLabel.textColor = [UIColor blueColor];
button.titleLabel.font = [UIFont systemFontOfSize:9.0];
NSString *name = [NSString stringWithFormat:#"%#",itemObj.itemStatus];
itmName = [NSString stringWithFormat:#"%#",itemObj.itemName];
NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];
[button setTitle:date forState:UIControlStateNormal];
button.userInteractionEnabled = NO;
button setBackgroundImage:[UIImage imageNamed:#"not_available_bttn_bck_img"] forState:UIControlStateNormal];
[imgView addSubview:button];
}
may it help you
There are 3 suggestions for you here:
Try loading images in background thread
Check this response Does iOS 5 have garbage collection?
Use leak, instrument to find out where your application is leaking, and then manage that part for the best
This is the bug of Apple.
UIScrollView will LEAK even these codes:
UIScrollView *s = [[UIScrollView alloc] initWithFrame:self.view.bounds];
s.contentSize = CGSizeMake(320, 800);
[self.view addSubview:s];
[s release];

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).

Changing bounds of imageView of UITableViewCell

I'm trying to place various size images inside imageView of UITableViewCell. I get the image data asynch'ly, create the image, set the content mode of imageView and finally set bounds of imageView. But the code seems insensitive to any changes I made. I want the images to be centered in a 75x75 area. I wrote the below code for this purpose
UIImage* image = [UIImage imageWithData:data];
[holder.imageView setContentMode:UIViewContentModeCenter || UIViewContentModeRedraw];
[holder.imageView setImage:image];
[holder.imageView setBounds:CGRectMake(0,0,75,75)];
[holder.imageView setFrame:CGRectMake(0,0,75,75)];
[holder setNeedsLayout];
Where holder is the UITableViewCell. The result I get is always the same. All images have 75px height and different widths. Can someone help me solve this problem?
I have realized that setting contentMode and bounds properties does not have any effect in that code. I have added an NSLog after the last line and got the results as below:
NSLog(#"imageview:%# bounds and contentMode:%# %#",[holder imageView],[holder.imageView bounds],[holder.imageView contentMode]);
imageview:<UIImageView: 0x39ab8a0;
frame = (0 0; 75 75); opaque = NO;
userInteractionEnabled = NO; layer =
<CALayer: 0x39a92b0>> bounds and
contentMode:(null) (null)
Still no solution
Done, I finally found the solution, it cost me 3 hours though =)
The solution is to change properties like bound,frame,contentMode in -(void)layoutSubviews method of the custom UITableViewCell class. The "trick" is to write layout code in this method, otherwise the code does not have any effect.
Below code did the work for me. It makes rows of the table vertically aligned.
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.bounds = CGRectMake(0,0,75,75);
self.imageView.frame = CGRectMake(0,0,75,75);
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
CGRect tmpFrame = self.textLabel.frame;
tmpFrame.origin.x = 77;
self.textLabel.frame = tmpFrame;
tmpFrame = self.detailTextLabel.frame;
tmpFrame.origin.x = 77;
self.detailTextLabel.frame = tmpFrame;
}
So the problem with UITableViewCell's is that you have no control over the size of the built-in objects (namely imageView, contentView, accessoryView, backgroundView). When the table changes, your customizations get trampled over.
You can, as Behlul pointed out, force the sizes to be correct by using layoutSubviews, but the problem with that is that layoutSubviews is called every time the table scrolls. That is a lot of unnecessary re-layout calls.
An alternate, method is to add all of your content to the contentView. Similarly if you are customizing the background, you can create a transparent backgroundView and add your custom background view (eg myBackgroundView) as a subview of backgroundView.
This way you can place and size your items how you want them.
The down side is the stock messages are no longer received from the accessory or image views. You just have to create you own.
Hope that helps!
// This code is not tested
// MyCustomTableViewCell
- (id) init{
self = [super initWithStyle: UITableViewCellStyleDefault reuseIdentifier:#"MyReuseIdentifier"];
if(self){
//image view
my_image_view = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"default_image.png"]] retain];
[my_image_view setFrame:CGRectMake(10,10,30,30)];
[self.contentView addSubview:my_image_view];
//labels
my_text_label = [[[UILabel alloc] initWithFrame:CGRectMake(50,10,100,15)] retain];
[self.contentView addSubview:my_text_label];
//set font, etc
//detail label
my_detail_label = [[[UILabel alloc] initWithFrame:CGRectMake(50,25,100,15)] retain];
[self.contentView addSubview:my_detail_label];
//set font, etc
//accessory view
//Whatever you want to do here
//attach "accessoryButtonTapped" selector to button action
//background view
UIView* background_view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 50)] autorelease];
[background_view setBackgroundColor:[UIColor greenColor]];
background_view.layer.cornerRadius = 17;
background_view.layer.borderWidth = 3;
background_view.layer.borderColor = [UIColor whiteColor].CGColor;
[self setBackgroundView:[[[UIView alloc] init] autorelease]];
[self.backgroundView addSubview:background_view];
}
return self;
}
- (void) setLabelText: (NSString*) label_text{
[my_text_label setText:label_text];
}
- (void) setDetailText: (NSString*) detail_text{
[my_detail_label setText: detail_text];
}
- (void) accessoryButtonTapped{
//call table view delegate's accessoryButtonTappedForRowWithIndexPath method
}
"UIViewContentModeCenter || UIViewContentModeRedraw" is equivalent to 1. It's also not a bitfield. You want UIViewContentModeCenter.
UITableViewCell.imageView is managed by the cell. If you want custom layout, try adding a view to contentView (I'm guessing what you mean by "centered in a 75x75 area"):
UIImageView * iv = [[[UIImageView alloc] initWithImage:image] autorelease];
iv.frame = (CGRect){{0,0},{75,75}};
iv.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin| UIViewAutoresizingFlexibleRightMargin;
iv.contentMode = UIViewContentModeScaleAspectFit;
[holder.contentView addSubview:iv];
try changing the "contentMode" property of imageView to 'UIViewContentModeScaleAspectFit' or 'UIViewContentModeScaleAspectFill'
Create subclass of UITableViewCell:
#interface UITableViewCellSubClass : UITableViewCell
#end
#implementation UITableViewCellSubClass
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(0,4,32,32);
self.textLabel.frame = CGRectMake(42,4,300,32);
}
#end

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.