Customizing UITableViewCell - iphone

I'm attempting to customize a UITableViewCell to "pretty up" an app, and I'm running into trouble. My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [tableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if ( cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (row == 0 && row == sectionRows - 1) {
rowBackground = [UIImage imageNamed:#"topAndBottomRow.png"];
selectionBackground = [UIImage imageNamed:#"topAndBottomRowSelected.png"];
NSLog(#"topAndBottom");
}
else if ( row == 0 ) {
rowBackground = [UIImage imageNamed:#"topRow.png"];
selectionBackground = [UIImage imageNamed:#"topRowSelected.png"];
NSLog(#"topRow");
}
else if ( row == sectionRows - 1 ) {
rowBackground = [UIImage imageNamed:#"bottomRow.png"];
selectionBackground = [UIImage imageNamed:#"bottomRowSelected.png"];
NSLog(#"bottomRow");
} else {
rowBackground = [UIImage imageNamed:#"middleRow.png"];
selectionBackground = [UIImage imageNamed:#"middleRowSelected.png"];
NSLog(#"middleRow");
}
((UIImageView *)cell.backgroundView).image = rowBackground;
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
NSString *cellValue = [[listOfItems objectAtIndex:indexPath.row] description];
cell.textLabel.text = cellValue;
return cell;
}
It selects the appropriate image, as shown in the console log, but I have two problems:
1. Setting the (cell.backgroundView).image has no effect
2. Setting the (cell.selectedBackgroundView).image errors out with:
-[UITableViewCellSelectedBackground setImage:]: unrecognized selector sent to instance 0x4b90670
I can find hints about UIView issues with this in google, but haven't found anything about a UITableViewCell. Help?

There's some incompatibilities - there isn't an official background image you can set, IIRC, there's only a background UIView. If you want something that's a UIImageView, you have to do it yourelf.
So, instead, you have to manually set the cell's backgroundView to be a blank UIImageView *when you create the cell, e.g.:
cell.backgroundView = [[[UIImageView alloc] init] autorelease];
...which you can then set images upon.

Related

UITableView experiences choppy scrolling when cell has a UIImageView subview

This has been driving me crazy for the better part of the day.
I've got a UITableView with UIImageViews. These imageviews load a locally saved PNG-file in the cellForRow-function of the tableview, and this works fine except the tableview will stop scrolling for a fraction of a second when a cell with an image in it scrolls into sight so to speak. I've trawled StackOverflow and google for an answer but I've come up short - so any help will be greatly appreciated.
Here is my code for the CellForRow-function:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if([currSection isEqualToString:#"composer"]){
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
cell.textLabel.hidden = YES;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil){
//no custom image in this cell - go with default background image
whiteView.image = [UIImage imageNamed:#"cellback2.png"];
whiteView.backgroundColor = [UIColor whiteColor];
}else{
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
NSData *data = [[NSData alloc] initWithContentsOfFile:s.slideImage];
UIImage *im = [[UIImage alloc] initWithData:data];
whiteView.image = im;
whiteView.image = [self imageWithImage:whiteView.image CovertToSize:CGSizeMake(204.8,153.6)];
whiteView.backgroundColor = [UIColor whiteColor];
}
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
There are a couple of changes to be made, first off the UIImageViews should be added when the cell is generated, rather than every time tableView:cellForRowAtIndexPath: is hit (as #Vishy suggests). Secondly you should cache the images you are loading from the documents directory ([UIImage imageNamed:] does this automatically for bundle resources).
#interface MyViewController () {
NSMutableDictionary *_imageCache;
}
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
// other viewDidLoad stuff...
_imageCache = [[NSMutableDictionary alloc] init];
}
- (void)viewDidUnload {
[super viewDidUnload];
// other viewDidUnload stuff...
[_imageCache release];
_imageCache = nil;
}
- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
whiteView.tag = 111;
whiteView.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.textLabel.hidden = YES;
}
UIImageView* iView = (UIImageView*) [cell.contentView viewWithTag:111];
if([currSection isEqualToString:#"composer"]) {
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil) {
//no custom image in this cell - go with default background image
iView.image = [UIImage imageNamed:#"cellback2.png"];
}
else {
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
// use the image path as the cache key
UIImage *theImage = [_imageCache objectForKey:s.slideImage];
if (theImage == nil) {
// load the image and save into the cache
theImage = [UIImage imageWithContentsOfFile:s.slideImage];
theImage = [self imageWithImage:theImage CovertToSize:CGSizeMake(204.8, 153.6)];
[_imageCache setObject:theImage forKey:s.slideImage];
}
iView.image = theImage;
}
}
}
#end
As a general rule, tableView:cellForRowAtIndexPath: is a method you need to get out of fast, so loading images from disk should be avoided wherever possible.
Change the code as per below...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyIdentifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
UIImageView *whiteView = [[UIImageView alloc] initWithFrame:CGRectMake((projectsTable.frame.size.width/2)-150, 4, 204.8, 153.6)];
whiteView.tag = 111;
whiteView.backgroundColor = [UIColor whiteColor];
[cell.contentView addSubview:whiteView];
[whiteView release];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.textLabel.hidden = YES;
}
UIImageView* iView = (UIImageView*) [cell.contentView viewWithTag:111];
if([currSection isEqualToString:#"composer"])
{
MySlide *s = [slidesArray objectAtIndex:indexPath.row];
if([s.slideImage isEqualToString:#""] || s.slideImage == nil)
{
//no custom image in this cell - go with default background image
iView.image = [UIImage imageNamed:#"cellback2.png"];
}
else
{
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
iView.image = [UIImage imageWithContentsOfFile:s.slideImage];
iView.image = [self imageWithImage:iView.image CovertToSize:CGSizeMake(204.8,153.6)];
}
}
}
There is also a delay the first time each UIImage is displayed. See this post for details on prompting the work at cache time rather than display time:
Setting image property of UIImageView causes major lag

The difference between two ways to access UIImageView under UITableViewCell of UITableView

I have a custom tableview to load news through webservice. I change image in each cell in method cellForRowAtIndexPath. After I got the right cell, I can access the image by two ways:
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = news.image;
or directly:
cell.imageView.image = news.image;
when I use the second way, some images get into wrong cell while the first way give me a perfect result. It also happend in a callback method iconDownLoadFinsh.
I feel confused about it. Can anyone try to explain it? Please do not hesitate to ask questions. Thanks in advance.
- (void)iconDownLoadFinsh:(NSData *)imageData row:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UIImageView *imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = [UIImage imageWithData:imageData];
NewsModel *newsModel = [newsArray objectAtIndex:indexPath.row];
newsModel.image = [UIImage imageWithData:imageData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"NewsCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NewsTableViewCell" owner:self options:nil];
cell = newsTableViewCell;
newsTableViewCell = nil;
}
// read from newsModel
NewsModel *news = [newsArray objectAtIndex:indexPath.row];
UILabel *label;
label = (UILabel *)[cell viewWithTag:10];
label.text = [NSString stringWithFormat:news.title];
label = (UILabel *)[cell viewWithTag:11];
label.text = [NSString stringWithFormat:news.description];
UIImageView *imageView;
imageView = (UIImageView *)[cell viewWithTag:12];
imageView.image = news.image;
if (news.image == nil)
{
imageView.image = [UIImage imageNamed:IconPlaceHolder];
// if (self.tableView.dragging == NO && self.tableView.decelerating == NO) {
IconDownLoader *iconDownLoader = [[IconDownLoader alloc] init];
iconDownLoader.url = news.imageUrl;
iconDownLoader.delegate = self;
iconDownLoader.indexPath = indexPath;
[downloadArray addObject:iconDownLoader];
[iconDownLoader start];
// }
}
return cell;
}
I generally always reset the cell properties in the method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
So I will do the following after getting the cell
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NewsTableViewCell" owner:self options:nil];
cell = newsTableViewCell;
newsTableViewCell = nil;
}
cell.imageView.image = nil; // Here resetting the imageview's image to nil.
Because you are reusing cells and if you do not clear old data, the cell data may get corrupt.

UITableViewCell customization - change background

I'm sure this is a question that been asked and answered, but I can't seem to find what I need. I am trying to customize the background of my UITableView, so to do this, I created a subclass of UITableViewCell, and built my custom cell with the IB. I successfully connected the custom cell to the table view, so the cell I get is getting displayed. I am trying to change the background of each cell, so here is what I use:
if(row == 1){
rowBackground = [UIImage imageNamed:#"VehicleExpenses_button.png"];
selectionBackground = [UIImage imageNamed:#"VehicleExpenses_button_selected.png"];
((UIImageView *)cell.backgroundView).image = rowBackground;
// ((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
}
within the
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method of my Controller class. I checked with the debugger, and all the lines in the code are getting called...
Any ideas?
This is the entire method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"HomeViewCell";
static NSString *CellNib = #"HomeViewCell";
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger row = [indexPath row];
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = (UITableViewCell *)[nib objectAtIndex:0];
}
if(row == 1){
rowBackground = [UIImage imageNamed:#"VehicleExpenses_button.png"];
selectionBackground = [UIImage imageNamed:#"VehicleExpenses_button_selected.png"];
((UIImageView *)cell.backgroundView).image = rowBackground;
// ((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
}
// perform additional custom work...
return cell;
}
Maybe I'm wrong but I don't see where you initialized the backgroundView as a UIImageView. Here is what I would add to the lines where you create the cells:
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = (UITableViewCell *)[nib objectAtIndex:0];
cell.backgroundView = [[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, tableView.bounds.size.width, cellHeight)] autorelease];
cell.selectedBackgroundView = [[[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, tableView.bounds.size.width, cellHeight)] autorelease];
}
Note: replace "cellHeight" with your desired height.
I hope this helps.
You can use this:
UIImageView *cellBackGround = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"selectedRow.png"]];
[[tableView cellForRowAtIndexPath:indexPath] setBackgroundView:cellBackGround];
[cellBackGround release];
Small problem. Instead of casting and setting the backgroundView and selectedBackgroundView's image property, set them to the UIImageView created from your images.
cell.backgroundView = [[[UIImageView alloc] initWithImage:rowBackground] autorelease];
cell.selectedBackgroundView = [[[UIImageView alloc] initWithImage:selectionBackground] autorelease];

iPhone table view cell style based on cell data

I'm trying to figure out how to display a particular type of image in a cell based on the text contents of the cell. For example, this method from the tutorial book I am working with will allow me to display the star image for the first 3 cells:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
if (row < 3) {
UIImage *image = [UIImage imageNamed:#"star.png"];
cell.imageView.image = image;
}
cell.textLabel.text = [listData objectAtIndex:row];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
return cell;
}
Is there any way I can get the actual cell contents here? So For example, if the cell contained the text 'bob', i could display a different image rather than a star?
Well it seems like you can look at your listData object and decide there. So you can try something like this
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
if ([[listData objectAtIndex:row] isEqualToString:#"bob"]) {
UIImage *image = [UIImage imageNamed:#"bob.png"];
cell.imageView.image = image;
}
else if (row < 3) {
UIImage *image = [UIImage imageNamed:#"star.png"];
cell.imageView.image = image;
}
cell.textLabel.text = [listData objectAtIndex:row];
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
return cell;
}
Maybe you should setup a dictionary that contains the names of the people as the key for the dictionary and the image name as the value. Then you could grab the cell's text and use that as the lookup for the dictionary.
After you set the text:
if([cell.textLabel.text rangeOfString:#"bob"].location != NSNotFound) {
UIImage *image = [UIImage imageNamed:#"bob.png"];
cell.imageView.image = image;
}
else {
UIImage *image = [UIImage imageNamed:#"star.png"];
cell.imageView.image = image;
}

First row in a UITableview, different from other rows

My UITableView nedd to have, in the first row, an image.
What's the problem? that when the user scrolls down the tableview and, after, he scrolls up, over the image there are the informations of the other rows!
Do you know why?
This is my code (i'm using a UITableViewCell)
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row != 0) {
return 73.0;
}
else {
return 109.0;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
if (indexPath.row == 0) {
[[NSBundle mainBundle] loadNibNamed:#"customcell_3" owner:self options:NULL];
cell = cellaNib_with_image;
}
else {
[[NSBundle mainBundle] loadNibNamed:#"customcell_2" owner:self options:NULL];
cell = cellaNib;
}
}
if (indexPath.row == 0) {
UIImage *rowBackground;
UIImage *selectionBackground;
rowBackground = [UIImage imageNamed:#"image.png"];
selectionBackground = [UIImage imageNamed:#"image.png"];
cell.backgroundView = [[[UIImageView alloc] init] autorelease];
((UIImageView *)cell.backgroundView).image = rowBackground;
cell.selectedBackgroundView = [[[UIImageView alloc] init] autorelease];
((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
}
else {
NSString *elemento = [NSString stringWithFormat:#"%#", [array objectAtIndex:indexPath.row]];
UILabel *testoLabel = (UILabel*)[cell viewWithTag:1];
testoLabel.text = elemento;
//ecc... here a take the other datas
}
return cell;
}
Thanks!
When cells are scrolled, they are reused.
So, first cell may be reused for other cells, and vice versa.
I would use two CellIdentifiers, one for first row, and second for rest of the rows.
If indexPath.row == 0, create/dequeue a cell using CellID1, and configure that and return.
If indexPath.row >1, create/dequeue using CellID2, configure this and return.
If you want to keep using single cellID, then before configuring them, nil/reset all the content first so that previous data are removed.