How to reliably subclass UITableViewCell for grouped UITableView? - iphone

When writing a customized subclass of UITableViewCell, I find that the results work well for the rectangular cells of a plain-styled UITableView, but do not work at all for the rounded cells in a grouped-styled table.
Is there a way to reliably subclass UITableViewCell to draw cells which work for grouped-style tables? (Without using Interface Builder.)

Could the answer be as simple as first calling [super layoutSubviews] inside your UITableViewCell subclass’s layoutSubviews method?
Here is my code.
First I create the UITextField and add it to the contentView in the initWithStyle: method:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
inputField = [[UITextField alloc] initWithFrame:CGRectZero];
[self.contentView addSubview:inputField];
inputField.borderStyle = UITextBorderStyleLine;
[inputField release];
}
return self;
}
Then in layoutSubviews, I’ve got this:
-(void)layoutSubviews
{
inputField.frame = CGRectMake(5, 5, 100, 20);
}
With that code, the text field is 5px from the left of the screen, which is, of course, 5px to the left of the table cell when it’s in grouped mode. In other words, OUTSIDE of the table view cell. No good.
Use this code and the inputField is placed 5px to the right of the cell, like I want it to be:
-(void)layoutSubviews
{
[super layoutSubviews]; // the magic line
inputField.frame = CGRectMake(5, 5, 100, 20);
}
I could have totally misunderstood the problem you were having, though!
Erik

I used to have lots of problems with UITableViewCell subclasses, but then I just stopped subclassing.
Adding subviews to the contentView property of a UITableViewCell seems to accomplish the same thing in any instance that I've run across, so I just do that inside my UITableViewController.
Here's an example that has a title and value:
- (UITableViewCell *)tableView:(UITableView*)tableView
cellForRowAtIndexPath: (NSIndexPath*)indexPath
{
static NSString* CellIdentifier = #"AccountDetailsCell";
UILabel* mainLabel = nil;
UILabel* valueLabel = nil;
const CGFloat kAccountDetailFontSize = 14.0;
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if ( cell == nil )
{
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake( 10.0, 0.0, 150.0, 44.0 )] autorelease];
mainLabel.tag = MAINLABEL_TAG;
mainLabel.font = [UIFont boldSystemFontOfSize: kAccountDetailFontSize];
mainLabel.textAlignment = UITextAlignmentLeft;
mainLabel.textColor = [UIColor darkGrayColor];
mainLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight;
mainLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview: mainLabel];
valueLabel = [[[UILabel alloc] initWithFrame: CGRectMake( 150.0, 0.0, 150.0, 44.0 )] autorelease];
valueLabel.tag = VALUELABEL_TAG;
valueLabel.font = [UIFont boldSystemFontOfSize: kAccountDetailFontSize];
valueLabel.textAlignment = UITextAlignmentRight;
valueLabel.textColor = [UIColor darkTextColor];
valueLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
valueLabel.backgroundColor = [UIColor clearColor];
[cell.contentView addSubview: valueLabel];
}
else
{
mainLabel = (UILabel*)[cell.contentView viewWithTag: MAINLABEL_TAG];
valueLabel = (UILabel*)[cell.contentView viewWithTag: VALUELABEL_TAG];
}
mainLabel.text = (NSString*)kCellTitles[indexPath.section][indexPath.row];
valueLabel.text = [self tableView: tableView valueLabelTextForRowAtIndexPath: indexPath];
return cell;
}

I've noticed this problem as well. My workaround has been to just make my table cells smaller (300 width instead of 320). It's not a great solution, but it works well.
I don't believe you can get rid of the table view insets on an individual basis when in "grouped" mode. I could be wrong though!

What problem are you having? In drawRect you are given a rect, and know your total size - just conform to that space. If you are using layoutSubviews, same thing.

Related

UITableView performance issues when adding UIViews to cell.contentView

I am experiencing performance problems when using some subviews on my UITableViewCells. After I keep scrolling it eventually starts getting very slow.
First step I am doing is creating a common UIView for every cell, essentially this is creating a white cell with a rounded effect on the cell with a shadow. The performance for this seems to be normal so I don't think it's the culprit.
Here is the code I am using to do this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *NewsCellIdentifer = #"NewsCellIdentifier";
NewsItem *item = [self.newsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NewsCellIdentifer];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NewsCellIdentifer];
cell.contentView.backgroundColor = [UIColor clearColor];
UIView *whiteRoundedCornerView = [[UIView alloc] initWithFrame:CGRectMake(10,10,300,100)];
whiteRoundedCornerView.backgroundColor = [UIColor whiteColor];
whiteRoundedCornerView.layer.masksToBounds = NO;
whiteRoundedCornerView.layer.cornerRadius = 3.0;
whiteRoundedCornerView.layer.shadowOffset = CGSizeMake(-1, 1);
whiteRoundedCornerView.layer.shadowOpacity = 0.5;
[cell.contentView addSubview:whiteRoundedCornerView];
[cell.contentView sendSubviewToBack:whiteRoundedCornerView];
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
cell.layer.opaque = YES;
cell.opaque = YES;
}
[cell.contentView addSubview:[self NewsItemThumbnailView:item]];
return cell;
}
Here is the method that returns the thumbnail view of the graphic and text:
- (UIView *) NewsItemThumbnailView:(NewsItem *)item
{
UIView *thumbNailMainView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 50, 70)];
UIImageView *thumbNail = [[UIImageView alloc] initWithImage:[UIImage imageNamed:item.ThumbNailFileName]];
thumbNail.frame = CGRectMake(10,10, 45, 45);
UILabel *date = [[UILabel alloc] init];
date.frame = CGRectMake(10, 53, 45, 12);
date.text = item.ShortDateString;
date.textAlignment = NSTextAlignmentCenter;
date.textColor = [BVColors WebDarkGrey];
CGFloat fontSize = 10.0;
date.font = [BVFont Museo:&fontSize];
date.opaque = YES;
thumbNail.opaque = YES;
thumbNailMainView.opaque = YES;
[thumbNailMainView addSubview:thumbNail];
[thumbNailMainView addSubview:date];
return thumbNailMainView;
}
The performance problem seems to be when I add the thumbnail view to the cell because when I comment that line out, I don't seem to have it. The thumbnail information is dynamic and will change with each cell. I would appreciate any advice on how I should do this without degrading the performance.
UITableView will call tableView:cellForRowAtIndexPath: each time a cell comes into view, and dequeueReusableCellWithIdentifier: will reuse existing cell objects if they are available. These two facts combine to put you in a scenario where every time you scroll, the same finite number of cell objects end up with an increasing number of subviews.
The proper approach is to create a custom UITableViewCell subclass that has a property for thumbnailView. In the setter for that property, remove the previous thumbnail (if any) and then add the new one to the contentView. This ensures that you'll only ever have one thumbnail subview at any time.
A less optimal approach would be adding a tag to the UIView returned from NewsItemThumbnailView (thumbNailMainView.tag = someIntegerConstant) and then searching for any view with that tag and removing it before adding another:
// remove old view
UIView *oldThumbnailView = [cell.contentView viewWithTag:someIntegerConstant];
[oldThumbnailView removeFromSuperview];
// add new view
[cell.contentView addSubview:[self NewsItemThumbnailView:item]];
I ended up leveraging a solution found on this stackoverflow post:
How should I addSubview to cell.contentView?
Essentially when the cell is first initialized I am setting the view as mentioned by Nishant; however once the cell is reused I am extracting out the items I need to change, such as an UIImageView and then a UILabel. Since these are pointers I can modify just what I need when I need to and the performance is fast again. Here is a abbreviated version of what I did.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *NewsCellIdentifer = #"NewsCellIdentifier";
NewsItem *item = [self.newsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NewsCellIdentifer];
UIView *thumbNailMainView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 50, 70)];
UIImageView *thumbNail;
UIView *textMainView = [[UIView alloc] initWithFrame:CGRectMake(20,20,80,80)];
UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(52,-5, 70, 20)];
UILabel *teaserLabel = [[UILabel alloc] initWithFrame:CGRectMake(50,20, 210, 40)];
UIView *newsItemCornerMainView = [[UIView alloc] initWithFrame:CGRectMake(255.7, 55.2, 55, 55)];
UIImageView *cornerIconView;
// If the cell doesn't existing go ahead and make it fresh.
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NewsCellIdentifer];
// Configure all the various subviews
..... //Sample below
// Make the title view
headerLabel.text = item.Title;
CGFloat textfontSize = 16.0f;
headerLabel.font = [BVFont Museo:&textfontSize];
headerLabel.textColor = [BVColors WebBlue];
headerLabel.textAlignment = NSTextAlignmentLeft;
headerLabel.numberOfLines = 0;
headerLabel.tag = 50;
// Make the Teaser view
teaserLabel.text = item.Teaser;
teaserLabel.numberOfLines = 0;
CGFloat tfontSize = 13.0f;
teaserLabel.textAlignment = NSTextAlignmentLeft;
teaserLabel.textColor = [BVColors WebDarkGrey];
teaserLabel.font = [BVFont HelveticaNeue:&tfontSize];
[teaserLabel sizeToFit];
teaserLabel.tag = 51;
[textMainView addSubview:headerLabel];
[textMainView sendSubviewToBack:headerLabel];
[textMainView addSubview:teaserLabel];
[cell.contentView addSubview:textMainView];
....
}
thumbNail = (UIImageView *) [cell viewWithTag:47];
[thumbNail setImage:[UIImage imageNamed:item.ThumbNailFileName]];
headerLabel = (UILabel *) [cell viewWithTag:50];
headerLabel.text = item.Title;
teaserLabel = (UILabel *) [cell viewWithTag:51];
teaserLabel.text = item.Teaser;
cornerIconView = (UIImageView *) [cell viewWithTag:48];
[cornerIconView setImage:[UIImage imageNamed:item.CornerIconFileName]];
return cell;
}
You should change thumbNailMainView content only everytime but you should not add its content on cell everytime.
So add this line where you are allocating cell
[cell.contentView addSubview:[self NewsItemThumbnailView:item]];
add this inside braces. and then access thumbNailMainView from cell and pass that item data which you need to change for each cell.
Assign a tag to thumbNailMainView and its subview thumbNail then access it as
UIView *_thumbNailMainView = [cell.contentView viewWithTag:_thumbNailMainView_tag];
UIImageView *_thumbNail = [_thumbNailMainView viewWithTag:thumbNail_tag];
_thumbNail.image = [UIImage imageNamed:item.ThumbNailFileName];
Hope it helps you.

Custom label background gets covered when cell is selected

I'm having a weird problem with my cells when it gets highlighted.
Just to introduce you better what the problem is, take a look at these two pictures:
This is how the cell looks like when it's not selected (normal state): http://cl.ly/0n193u3U1o403x1s0m3z
This is how the cell looks when it's highlighted (during tap): http://cl.ly/1o2U400D3L0b3n3m1N1J
As you can see, the background of the second label seems to get ignored when the cell is selected.
I don't want this to happen. I just want the 2nd label to stay there and remain as it is.
This is how I create the cells (there are several types each of them using different cell identifier).
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellTableIdentifier] autorelease];
cell.backgroundView = [[[UIView alloc] init] autorelease];
}
switch (indexPath.section) {
...
...
case kTableSectionPending:
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10, 10, self.tableView.frame.size.width - 20, 30.0)] autorelease];
UILabel *label2 = [[[UILabel alloc] initWithFrame:CGRectMake(10, 50, self.tableView.frame.size.width - 20, 30.0)] autorelease];
label.text = NSLocalizedString(#"IncompletePath", #"");
label.font = [UIFont boldSystemFontOfSize:16];
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
label.layer.cornerRadius = 10;
FFRoute *lastPoint = ((FFRoute *) [[[FFSQLite sharedSingleton] getRoutesFromItinerary:itinerary] lastObject]);
label2.text = [NSString stringWithFormat:#"%#: %#", NSLocalizedString(#"LastPoint", #""), [self getStringFromTimestampOfFFRoute:lastPoint]];
label2.font = [UIFont systemFontOfSize:15];
label2.textAlignment = UITextAlignmentCenter;
label2.textColor = [UIColor whiteColor];
label2.backgroundColor = [UIColor colorWithRed:68/255.f green:82/255.f blue:124/255.f alpha:1];
label2.layer.cornerRadius = 10;
[label2 setOpaque:YES];
[cell.contentView addSubview:label];
[cell.contentView addSubview:label2];
// Set up background color
UIColor *bgcolor = [UIColor colorWithRed:250/255.f green:212/255.f blue:137/255.f alpha:1];
cell.backgroundView.backgroundColor = bgcolor;
break;
}
I tried to set to true the Opaque property with no luck.
What am I missing? Thank you
When you create the cell, do:
cell.selectionStyle = UITableViewCellSelectionStyleNone;
A cell can be highlighted, selected or both. You might want to customize your drawing so that it draws in a selected state (but draws as usual when only in highlighted state).
In iOS 6, you can use 2 methods in UITableViewDelegate to customise highlighting for the cell, and also its subviews:
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell setBackgroundColor:[UIColor lightGrayColor]]; // Your highlight color
// Make changes to subviews in cell
}
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[UIView animateWithDuration:0.5f animations:^{
[cell setBackgroundColor:[UIColor whiteColor]]; // Your unhighlight
// Revert back changes for subviews
}];
}

How to change text color for Section Headers in a Grouped TableView in iPhone SDK?

I am making an iPhone app where in I have a grouped TableView with headers for the sections.
Problem is that I want to change the Section Header's text color.
How can I change the text color of Section Header?
What should I do?
Add the following code to your AppDelegate class in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
method:
[[UILabel appearanceWhenContainedIn:[UITableViewHeaderFooterView class], nil] setTextColor:[UIColor whiteColor]];
If you don't want to do it app wide like in Vahan's solution, here is a solution using one of UITableViewDelegate's method :
func tableView(tableView: UITableView, willDisplayHeaderView view:UIView, forSection: Int) {
if let headerView = view as? UITableViewHeaderFooterView {
headerView.textLabel?.textColor = UIColor.redColor()
}
}
This is SURELY gonna work for you.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *tempView=[[UIView alloc]initWithFrame:CGRectMake(0,200,300,244)];
tempView.backgroundColor=[UIColor clearColor];
UILabel *tempLabel=[[UILabel alloc]initWithFrame:CGRectMake(15,0,300,44)];
tempLabel.backgroundColor=[UIColor clearColor];
tempLabel.shadowColor = [UIColor blackColor];
tempLabel.shadowOffset = CGSizeMake(0,2);
tempLabel.textColor = [UIColor redColor]; //here you can change the text color of header.
tempLabel.font = [UIFont fontWithName:#"Helvetica" size:fontSizeForHeaders];
tempLabel.font = [UIFont boldSystemFontOfSize:fontSizeForHeaders];
tempLabel.text=#"Header Text";
[tempView addSubview:tempLabel];
[tempLabel release];
return tempView;
}
just copy and paste this function in your code.
You can implement this table view data source method:
- (UIView *)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section
{
//create your custom label here & anything else you may want to add
return YourCustomView;
}
I built off of the answer from #Harsh.
This is the closest I could get, indistinguishable from what I can tell.
It goes in the <UITableViewDataSource> obviously.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *hView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
hView.backgroundColor=[UIColor clearColor];
UILabel *hLabel=[[[UILabel alloc] initWithFrame:CGRectMake(19,17,301,21)] autorelease];
hLabel.backgroundColor=[UIColor clearColor];
hLabel.shadowColor = [UIColor whiteColor];
hLabel.shadowOffset = CGSizeMake(0.5,1); // closest as far as I could tell
hLabel.textColor = [UIColor blackColor]; // or whatever you want
hLabel.font = [UIFont boldSystemFontOfSize:17];
hLabel.text = #"Your title here"; // probably from array
[hView addSubview:hLabel];
return hView;
}
#Harsh 's answer worked great for me, and by changing the coordinations of UILabel you can move it around. Also, I, personally thought to change the shadow offset a bit to make it more readable, but that could be a personal choice. Here's my version in case:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
NSString *sectionTitle = [self tableView:tableView titleForHeaderInSection:section];
if (sectionTitle == nil) {
return nil;
}
// Create label with section title
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(40, -5, 300, 30)] autorelease];
//If you add a bit to x and decrease y, it will be more in line with the tableView cell (that is in iPad and landscape)
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor yellowColor];
label.shadowColor = [UIColor whiteColor];
label.shadowOffset = CGSizeMake(0.5., 0.5.);
label.font = [UIFont boldSystemFontOfSize:18];
label.text = sectionTitle;
// Create header view and add label as a subview
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, SectionHeaderHeight)]autorelease];
[view addSubview:label];
return view;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UILabel *myLabel = [[UILabel alloc] init];
myLabel.frame = CGRectMake(20, 8, 220, 20);
myLabel.font = [UIFont boldSystemFontOfSize:16];
myLabel.text = [self tableView:tableView
titleForHeaderInSection:section];
myLabel.backgroundColor=[UIColor grayColor];
UIView *headerView = [[UIView alloc] init];
[headerView addSubview:myLabel];
return headerView;
}

UITableViewCell repeats after scrolling

I am not sure why my UITableViewCell's are repeating after the user scrolls?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
ChartlyCell *cell = (ChartlyCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
//cell = [[[ChartlyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell = [[[ChartlyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier andDelegate:self] autorelease];
}
ChartlyObject *myChartlyObject;
myChartlyObject = [self.items objectAtIndex:indexPath.row];
cell.usernameLabel.text = myChartlyObject.userName;
cell.bodyLabel.text = myChartlyObject.chart_message;
[cell.messageLabel setText: myChartlyObject.chart_message];
cell.timeLabel.text = [Utils toShortTimeIntervalStringFromStockTwits: myChartlyObject.created_at];
[cell.chartImage loadImageFromURL:[NSURL URLWithString:myChartlyObject.imageThumbnail]];
return cell;
}
This is how the cell is being created:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier andDelegate:(id<NLabelDelegate>)ndelegate{
if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
// Initialization code
CGRect rect;
rect = CGRectMake(77, 5, 200, 20); //CGRectMake(10, 5, 200, 20);
usernameLabel = [[UILabel alloc] initWithFrame:rect];
//usernameLabel.backgroundColor = backgroundColor;
usernameLabel.font = [Utils getBoldSystemFontWithSize:14];
usernameLabel.textColor = [UIColor colorWithRed:.368 green:.741 blue:.784 alpha:1];
[self.contentView addSubview: usernameLabel];
rect = CGRectMake(277, 5, 38, 20);
timeLabel = [[UILabel alloc] initWithFrame:rect];
timeLabel.textAlignment = UITextAlignmentRight;
//timeLabel.backgroundColor = backgroundColor;
timeLabel.font = [Utils getBoldSystemFontWithSize:14];
timeLabel.textColor = [UIColor colorWithRed:.98 green:.65 blue:.01 alpha:1];
[self.contentView addSubview: timeLabel];
/*rect = CGRectMake(77, 25, 238, 68); //CGRectMake(10, 50, 238, 68);
bodyLabel = [[UILabel alloc] initWithFrame:rect];
bodyLabel.font = [Utils getSystemFontWithSize:10];
bodyLabel.numberOfLines = 3;
bodyLabel.lineBreakMode = UILineBreakModeWordWrap;
bodyLabel.textColor = [UIColor blackColor];
bodyLabel.backgroundColor = [UIColor clearColor];
[self.contentView addSubview: bodyLabel];*/
rect = CGRectMake(77, 25, 238, 68);
messageLabel = [[NLabel alloc] initWithFrame:rect andDelegate:ndelegate];
[messageLabel setOpaque:NO];
[messageLabel setBackgroundColor: [UIColor blackColor]];
//[messageLabel setTotalheight:68];
[self.contentView addSubview: messageLabel];
rect = CGRectMake(13, 10, 48, 48);
chartImage = [[AsyncImageView alloc]initWithFrame:rect];
[self.contentView addSubview: chartImage];
}
return self;
}
UPDATE:
What is odd, is that if I uncomment the bodyLabel block and comment out the messageLabel block the repeating cells don't appear anymore. I am not sure why and would still like to find a resolution
UPDATE #2
Only messageLabel gets repeated. All of the other labels do NOT repeat. Why is this?
Because you are re-using tablecells for proper memory management as you should be doing via dequeueReusableCellWithIdentifier :) This way you don't have a unique cell for every single cell you need. Instead you only have about 5 or 6 that get re-used over and over during scrolling.
When the cell comes back into view, you need to reset the state (i.e. update the info, labels, images, etc) of each cell based on whatever cell came into view.
One thing you can do is to remove all the views in the cell.contentView after you create the cell or dequeue it. Alternatively, you can create a custom cell with attributes for the labels that you can set to different values when you scroll down.

UITableViewCell background size

I'm trying to set the size of my background to be a little shorter than the default, creating some space between the cells. This has proven to be difficult. Setting the frame of the background view seems to do nothing:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseIdentifier = #"cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell)
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:reuseIdentifier] autorelease];
// Set up the cell...
cell.contentView.backgroundColor = [UIColor clearColor];
cell.backgroundView = [[[UIView alloc] initWithFrame:CGRectMake(0, 4, 320, 42)] autorelease];
cell.backgroundView.backgroundColor = [UIColor blackColor];
cell.backgroundView.alpha = .2;
cell.selectedBackgroundView = [[[UIView alloc] initWithFrame:CGRectMake(0, 4, 320, 42)] autorelease];
cell.selectedBackgroundView.backgroundColor = [UIColor whiteColor];
cell.selectedBackgroundView.alpha = .2;
cell.font = [UIFont fontWithName:#"MarkerFelt-Thin" size:22.0f];
cell.selectedTextColor = [UIColor blackColor];
cell.textColor = [UIColor whiteColor];
NSDictionary *dict = [files objectAtIndex:indexPath.row];
cell.text = [dict objectForKey:#"name"];
return cell;
}
Any help?
Also, setting the selected background view doesn't do anything. When a cell is selected, the background is completely blank. Why is this?
I'm using iPhone OS 2.2.1.
I also do this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor clearColor];
self.tableView.rowHeight = 50.0f;
}
You can download the code here (made a small project for this issue only):
http://dl.dropbox.com/u/608462/tabletest2.zip
The backgroundView is not a normal view, there's something going on behind the scenes. Check this link out:
Difference between background view and content view in uitableviewcell
Specifically, from the documentation:
backgroundView:
The default is nil for cells in plain-style tables (UITableViewStylePlain) and non-nil for grouped-style tables UITableViewStyleGrouped). UITableViewCell adds the background view as a subview behind all other views and uses its current frame location.
Hence: it doesn't really have a frame location, it uses the cell's frame location.
This code worked:
UIImageView *bgView = [[UIImageView alloc] init]; // Creating a view for the background...this seems to be required.
bgView.backgroundColor = [UIColor redColor];
cell.backgroundView = bgView;
UIImageView *bgImageView = [[UIImageView alloc] init]; // Creating a subview for the background...
bgImageView.backgroundColor = [UIColor colorWithWhite:1 alpha:1];
[bgImageView setFrame:CGRectInset(cell.bounds, 1, 1)];
[cell.backgroundView addSubview:bgImageView]; // Assigning the subview, and cleanup.
[bgImageView release];
[bgView release];
Spent about an hour trying to figure this out...but it works. This is code in the cellForRowAtIndexPath delegate method--I won't cover the whole thing here obviously.
morgancodes' solution led me into the right direction.
I added a sublayer to the background view and styled it. When setting the background color of the background view to clearColor, the sublayer is the only thing showing.
UIView *backgroundView = [[UIView alloc] init];
backgroundView.backgroundColor = [UIColor clearColor];
CALayer *sublayer = [CALayer layer];
sublayer.backgroundColor = [UIColor colorWithWhite:1 alpha:0.8].CGColor;
sublayer.frame = CGRectMake(15, 3, tableView.frame.size.width - 45, 38);
sublayer.cornerRadius = 5;
[backgroundView.layer addSublayer:sublayer];
cell.selectedBackgroundView = backgroundView;
Here's a completely different method from what you're trying.
One thing I like to do is use a custom image for the backgroundView and selectedBackgroundView, rather than let the iPhone handle the coloring tasks. This gives me a lot more flexibility on how the cell is rendered. All it takes is adding something like this:
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"normal.png"]];
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"selected.png"]];
To:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
Yet another approach: add a sublayer to your background. I added the following to the initialization of a UITableViewCell subclass and it seems to work great.
UIView* backgroundView = [[UIView alloc] initWithFrame: self.contentView.frame ];
backgroundView.layer.frame = CGRectInset(backgroundView.layer.frame, 20, 20);
CALayer *sublayer = [CALayer layer];
sublayer.backgroundColor = [UIColor colorWithWhite:0.69 alpha:1].CGColor;
sublayer.frame = CGRectMake(INDENT, 0, width - (INDENT * 2), [ChuckWagonTableViewCellCell cellHeight]) ;
[backgroundView.layer addSublayer:sublayer];
self.selectedBackgroundView = backgroundView;
Try this:
UIView *bg = [[UIView alloc] initWithFrame: CGRectInset(cell.frame, 0.0, 2.0)];
bg.backgroundColor = [UIColor whiteColor];
cell.backgroundView = bg;
Also don't forget to set background color and separator color to clear in viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.separatorColor = [UIColor clearColor];
self.tableView.backgroundColor = [UIColor clearColor];
}
When messing with the background view, I would do it in:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
rather than in:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Try using:
cell.backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 4.0, 320.0, 40.0)]];
For the second question, did you implement:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
What i think is happening, is that when you select a row, internally the selectedbackgroundview's alpha value is se to 1, thus showing it completely white.
I had a similar problem, and none of the answers seemed to fit in my case.
All my rows have the same height in this case, but with some math this could be adapted to accomodate rows with different heights.
I had set the height in my controller, by using the UITableViewDelegate method. I have an instance variable called cellBackgroundImage on my controller that is the UIImage that will be used for the UITableViewCell background. The UITableView background is set to [UIColor clearColor].
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return cellBackgroundImage.size.height + SPACING_HEIGHT;
}
Where SPACING_HEIGHT is a #define constant for the gap height.
Then, the trick was to use an UIView that would wrap the UIImageView that will be the cell's background. I accomplished this by doing:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"ContentCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:#"ContentCell"] autorelease];
CGFloat height = [self tableView:tableView heightForRowAtIndexPath:indexPath];
cell.frame = CGRectMake(0, 0, [UIScreen mainScreen].applicationFrame.size.width, height);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIView *backView = [[UIView alloc] initWithFrame:CGRectInset(cell.frame, 0, 0)];
UIImageView *imageView = [[UIImageView alloc] initWithImage:cellBackgroundImage];
[backView insertSubview:imageView atIndex:0];
cell.backgroundView = backView;
[backView release];
[imageView release];
}
return cell;
}
Then, by setting cell.backgroundView = backView to the UIView with the UIImageView that contains my background, i managed to achieve the gap effect between rows.
I hope this helps.
A possible solution could be to subclass UIView and add color and height arguments (if you only want to change the height, otherwise you can pass a size/rect). Note that a background color needs to be set, otherwise you'll see a blank area.
- (id)initWithColor:(UIColor *)color height:(CGFloat)height backgroundColor:(UIColor *)backgroundColor;
{
self = [super init];
if (self != nil)
{
_color = color;
_height = height;
_backgroundColor = backgroundColor;
}
return self;
}
Add the appropriate properties:
#interface CellSelectedBackgroundView ()
#property (strong, nonatomic) UIColor *color;
#property (strong, nonatomic) UIColor *backgroundColor;
#property (assign, nonatomic) CGFloat height;
#end
And in drawRect: you can fill the area:
- (void)drawRect:(CGRect)rect
{
[self.backgroundColor setFill];
UIRectFill(rect);
[self.color setFill];
CGRect frame = CGRectMake(0, 0, self.bounds.size.width, self.height);
UIRectFill(frame);
}
Simply initialize you custom UIView subclass and set it as the selectedBackgroundView property of your UITableViewCell.
Try adding a subview into your backgroundViews instead of modifying them directly:
UIView *selectedView = [[UIView alloc] initWithFrame:UIEdgeInsetsInsetRect(cell.frame, UIEdgeInsetsMake(8, 8, 8, 8))];
selectedView.backgroundColor = [UIColor redColor];
cell.selectedBackgroundView = [UIView new];
cell.selectedBackgroundView.backgroundColor = [UIColor clearColor];
[cell.selectedBackgroundView addSubview:selectedView];
I had the same problem as yours with the selectedBackgroundView and this worked for me ;)