Custom Cell, only last cell gets [layoutSubviews]? - iphone

I'm creating a Settings View for my app, and in that view is a UITableView. I'm creating custom cells to meet my needs, but I'm having issues - only the last cell is getting [layoutSubviews]. Am I doing something wrong?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//int type = (indexPath.row == 0?1:0);
//if(indexPath.row == 6) type = 2;
NSLog(#"row %i created", indexPath.row);
TableCell *cell = [[TableCell alloc] initWithType:indexPath.row];
cell.textLabel.text = #"Test cell";
return cell;
}
And in my custom cell:
#implementation TableCell
UIImageView *shadowView;
int row;
- (id) initWithType:(int)type {
row = type;
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
self.backgroundColor = [UIColor clearColor];
self.backgroundView = [[UIView alloc] init];
UIImage *shadowImage = [UIImage imageNamed:#"CellShadow"];
shadowImage = [shadowImage resizableImageWithCapInsets:UIEdgeInsetsMake(14, 14, 14, 14)];
shadowView = [[UIImageView alloc] initWithImage:shadowImage];
[self.contentView addSubview:shadowView];
//[self.contentView sendSubviewToBack:shadowView];
NSLog(#"agreed, row %i created", row);
[self layoutSubviews];
return self;
}
- (void) layoutSubviews {
NSLog(#"row: %i", row);
[super layoutSubviews];
shadowView.frame = CGRectMake(
0, 0,
self.contentView.frame.size.width,
self.contentView.frame.size.height
);
}
#end
Continuously, only the last cell #6, is reported when I rotate, or when layoutSubviews should be called. Any suggestions?

Do not call layoutSubviews directly. Use [self setNeedsLayout] or [self layoutIfNeeded]. But do not call these at all in the cell's init method.
Also, do not call [[TableCell alloc] initWithType:indexPath.row]; directly, either. Instead, use...
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath
Once you've built that cell, you can tell it it's row, but be aware that the cells get recycled as the table scrolls, so you must update that value on every call to cellForRowAtIndexPath.
The cells ought to get layout again (without you making any calls direct or indirect) when the table view is resized.
See the tableview doc here.

You should never call layoutSubviews directly, it will be called automatically by iOS once the cell is ready to display. You should also deque the cell as #danh is recommending. If you're not very comfortable with all this, then I'd really recommend you have a look at the free Sensible TableView framework, which automates creating these kind of settings views (I create mine in a couple of lines, really).

The issue was of my own poor code. Using cell.backgroundView helped a lot here.

Never Call layoutSubviews by yourself. It will be called when ever frames of subview in cell are changed. Even if just change the text of labels in your custom cell wont call layoutSubviews. Ue the deque of cells for reusing for better performance. As it wont allocate cell every time. And in you code looks like has lot of memory issues since cell allocated wont be released and new cell is created.

Related

Not refreshing screen in programmatically filled TableView

When I scroll down and up again my text in tableView will disappear.
And my code is:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [screenDefBuild.elementsToTableView count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
ScreenListElements *currentScreenElement = [screenDefBuild.elementsToTableView objectAtIndex:indexPath.row];
cell.textLabel.text = currentScreenElement.objectName;
currentRow++;
return cell;
}
- (void)viewDidLoad
{
[super viewDidLoad];
tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
[tableView setDataSource:self];
[self.view addSubview:tableView];
}
I also want to fill my table view to entire screen. (grey strap on the top).
I don't know what you're doing with this variable
currentRow++;
But whatever you use that for, i'd wager its breaking your code.
the UITableView will call cellForRowAtIndexPath every time a cell is about to appear on screen regardless of whether it has been on screen before or not. When you scroll down and then scroll back up this variable will have increased beyond the bounds of your data, hence you get empty cells.
You need to design this method in such a way that it can create any cell in the table view at any time. You can't rely on the order that the cells will be made and with scrolling you will have to make the same cell over and over again. Only use indexPath to figure out which cell you are currently supposed to be making.
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableView_Class/Reference/Reference.html
Answer for the second part of the question- grey strap. You are adding the table view to current view, so you should use the size property of self.view.frame but not the origin. You want this to be set to 0,0.
Change
tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
to
CGRect viewFrame=self.view.frame;
viewFrame.origin=CGPointZero;
tableView = [[UITableView alloc] initWithFrame:viewFrame];
As for the first part of your question- it's strange, as you seem to do everything properly. One thing i may suggest is to add [tableView reloadData]; in the end of viewDidLoad.

Custom UITableViewCell redraw issues

I have a custom UITableView cell that I've added a textbox to for editing, that shows and hides based on the edit mode. I've also tried adding a vertical line that shows when editing, and it does that, but I'm running into some drawing issues. I just added a green checkmark rightView to start working on input validation feedback, and I'm seeing similar issues.
Here is the code for the cell, and part of my cellForRowAtIndexPath.
#import <UIKit/UIKit.h>
#interface EditableCellStyle2 : UITableViewCell {
CGRect editRect;
UITextField *editField;
UIView *lineView;
}
#property (nonatomic, readonly, retain) UITextField *editField;
#property (nonatomic, readonly, retain) UIView *lineView;
#end
#import "EditableCellStyle2.h"
#implementation EditableCellStyle2
#synthesize editField;
#synthesize lineView;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code.
editRect = CGRectMake(83, 12, self.contentView.bounds.size.width-83, 19);
editField = [[UITextField alloc] initWithFrame:editRect];
editField.font = [UIFont boldSystemFontOfSize:15];
editField.textAlignment = UITextAlignmentLeft;
editField.textColor = [UIColor blackColor];
editField.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
[self.contentView addSubview:editField];
self.editField.enabled = NO;
self.editField.hidden = YES;
lineView = [[UIView alloc] initWithFrame:CGRectMake(80, 0, 1, self.contentView.bounds.size.height)];
self.lineView.backgroundColor = [UIColor lightGrayColor];
[self.contentView addSubview:lineView];
self.lineView.hidden = YES;
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state.
}
-(void)layoutSubviews
{
[super layoutSubviews]; // layouts the cell as UITableViewCellStyleValue2 would normally look like
editRect = CGRectMake(83, 12, self.contentView.frame.size.width-self.detailTextLabel.frame.origin.x-10, 19);
editField.frame = editRect;
}
- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
if (state & UITableViewCellStateEditingMask) {
self.detailTextLabel.hidden = YES;
self.editField.enabled = YES;
self.lineView.hidden = NO;
self.editField.hidden = NO;
}
}
- (void)didTransitionToState:(UITableViewCellStateMask)state {
[super didTransitionToState:state];
if (!(state & UITableViewCellStateEditingMask)) {
self.editField.enabled = NO;
self.editField.hidden = YES;
self.lineView.hidden = YES;
self.detailTextLabel.hidden = NO;
self.editField.text = self.detailTextLabel.text;
}
}
- (void)dealloc {
[editField release];
[lineView release];
[super dealloc];
}
#end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// handling every section by hand since this view is essentially static. Sections 0, 1, 2, and 4 use a generic editable cell.
// Section 3 uses the multiline address cell.
static NSString *CellIdentifier = #"Cell";
EditableCellStyle2 *cell = (EditableCellStyle2 *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (indexPath.section == 0 || indexPath.section == 1 || indexPath.section == 2 || indexPath.section == 4) {
if (cell == nil) {
cell = [[[EditableCellStyle2 alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
}
// Configure the Odometer
if (indexPath.section == 0) {
NSArray *array = [sectionsArray objectAtIndex:indexPath.section];
NSDictionary *dictionary = [array objectAtIndex:indexPath.row];
cell.textLabel.text = #"Odometer";
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", [dictionary objectForKey:#"Odometer"]];
cell.tag = kOdometer;
cell.editField.text = cell.detailTextLabel.text;
cell.editField.placeholder = #"Odometer";
cell.editField.tag = kOdometer;
cell.editField.keyboardType = UIKeyboardTypeNumberPad;
// Create a view for the green checkmark for odometer input validation and set it as the right view.
UIImage *checkImage = [UIImage imageNamed:#"tick.png"];
UIImageView *checkImageView = [[[UIImageView alloc] initWithImage:checkImage] autorelease];
cell.editField.rightView = checkImageView;
cell.editField.rightViewMode = UITextFieldViewModeAlways;
}
return cell;
}
There is more to it but all the cells are built the same way.
The problems are that, when in edit mode, the vertical lines will display properly. When I leave edit mode, any cells that were off screen when I go to normal mode still have the vertical line (it doesn't get hidden). Also, now that I've added the imageView for the checkmark indicator, any cells that are off screen when switching modes gain the checkmark. (only section 0 sets it up).
I've also noticed that if i do cell.setNeedsDisplay, the text label and detail text label won't update if the data source has been updated. I have to do [self.tableView reloadData] which skips any active animations.
I'm sure these issues are related to me using a custom cell + dequeueReusableCellWithIdentifier, but I can't find exactly what.
Any feedback or a push in the right direction would be appreciated.
Edit:
Not using reusable cells seems to have resolved the above issues. I'm still open to feedback on the cell code.
I forgot one other issue that may or may not be related. One of my cells has a "tap to view list" button. If I enter data into the cells while in edit mode, then hit that button to choose some info from a list (it displays a modal table view), when I dismiss the modal view, all of the cells' edited data has reverted to their original state. I'm not calling reload data when I dismiss the modal view controller. I thought this might be fixed by not using reusable cells but it isn't.
You need to prepare the cell for reuse. Try adding this to the EditableCellStyle2 implementation:
- (void)prepareForReuse {
[super prepareForReuse];
[self didTransitionToState:UITableViewCellStateDefaultMask];
}
Maybe you trimmed too much for your post, but in the posted code your reusable cell handling is all wrong.
First of all, each different type of cell needs its own CellIdentifier. In your case (judging from your code comment), that means at least a different identifier for section 3 versus sections 0, 1, 2, and 4. You may also want to do a separate identifier for section 0, so you don't have to keep removing and readding that checkmark. The different identifier needs to be used for both the dequeueReusableCellWithIdentifier: and initWithStyle:reuseIdentifier:` for the appropriate sections.
The second problem is that you are not resetting the cells correctly. There are two "kinds" of initialization that must be done to a UITableViewCell: initialization that is the same for every cell of its type, and initialization that depends on the specific row being displayed. The first kind can (and should) only be done once, when a new cell is allocated. The second kind must be done every time through tableView:cellForRowAtIndexPath:. You seem to be doing the first correctly for your EditableTableCell2 class in its init method, but I see nowhere in there where you do the per-row initialization: you never reset selected, or the cell state, or the contents of the edit field, or remove the checkImageView since you are using the same kind of cell for section 0 versus the other sections. If you want, the reset selected, state, and clearing out the checkbox image and field contents can be done in prepareForReuse on your EditableTableCell2 class.
The third problem, which is almost certainly due to over-trimming, is that you never create this "multiline address" cell for section 3. You'll end up maybe reusing a random EditableTableCell2, or maybe crashing on an exception from the framework when you return nil from tableView:cellForRowAtIndexPath:.

Force UITableView to dump all reusable cells

I have a UITableView where I have the backgroud color set via
UIView *myView = [[UIView alloc] init];
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
cell.backgroundView = myView;
[myView release];
The problem I find is that when I edit a table (via setEditing:YES...) some cells of the same color invariable are next to each other. How do I force UITableView to fully redraw. reloadData is not doing a great job.
Is there are deep-cleaning redraw?
I had this issue before so I'll share with you how I solved it:
You can use a boolean flag (say it's called needsRefresh) to control the behavior of cell creation in -cellForRowAtIndexPath:
An example:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell || needsRefresh) {
cell = [[[UITableViewCell alloc] init....] autorelease];
}
//.....
return cell;
}
So, when you need a hard reload, set the needsRefresh flag to YES. Simple as a pimple.
For me the accepted answer didn't really work since I had no idea when to set the needsRefresh back to YES.
What worked for me was:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:customCellIdentifier];
if(nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:customCellIdentifier];
}
//.....
return cell;
}
And then you change the customCellIdentifier value whenever you need to. This way the cells are also still reusable if you switch back to the original cell identifier.
The accepted method seems dirty, it just makes a bunch of new cells that are stored along with the bad ones. Here are a couple of solutions depending on your situation:
1.
first, for the situation described in the question you should not dump your cells and create new views on every cycle. You need to tag your view and then get it back when from the cell when you get a reuse cell:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell) {
cell = [[UITableViewCell alloc] init];
UIView *myView = [[UIView alloc] init];
cell.backgroundView = myView;
[myView setTag:5]; //<------
}
UIView *myView = [cell viewWithTag:5]; //<------
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
return cell;
}
//then just reload the tableview.
2.
...or even better, why not just use the cell backgrouncolor and update that without creating a view.
3.
A sure way to really clear out old cached cells it to simply recreate the UITableView object.
4.
In most cases you dont need to destroy these cells, just keep track of your elements and update them after getting the reusable cell.You can tag all your elements, keep a array reference to them, find them thought the view hierarchy... Im sure theres a bunch of other ways.
5.
heres a one liner to directly purge all cells, although not best practice to mess with the internals of objects like this as they might change in future versions:
[(NSMutableDictionary*)[tableview valueForKey:#"_reusableTableCells" ] removeAllObjects];
I was able to solve this by adding a refresh variable to the table datasource. I used a dictionary for each cell, but there's an extra key called #"refresh":#"1", indicating the cell needs refreshing. Once it's updated, I set that key's value to #"0". So whenever the table is reloaded, make sure the key goes back to #"0" again.
#define TABLE_VIEW_CELL_DEFAULT_ID #"cellIdentifier"
#property (nonatomic, strong) NSString *tableViewCellIdentifier;
#property (nonatomic) NSUInteger tableViewCellIdentifierCount;
// By using a different cell identifier, this effectively flushes the cell
// cache because the old cells will no longer be used.
- (void) flushTableViewCellCache
{
self.tableViewCellIdentifierCount++;
self.tableViewCellIdentifier = [NSString stringWithFormat:#"%#%i", TABLE_VIEW_CELL_DEFAULT_ID, self.tableViewCellIdentifierCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.tableViewCellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.tableViewCellIdentifier];
}
// rest of method...
}

How to set the table view cell accessory view to retain a previously initialized UIImageView?

Let's say I have a property in my view controller, defined as follows:
#property (nonatomic, retain) UIImageView *checkmarkOffAccessoryView;
I #synthesize this in the implementation, release it in -dealloc and initialize it in -viewDidLoad as follows:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
So far so good.
When I use it in my table view delegate as an accessory view for multiple cells, two things happen:
Only one cell's accessory view shows the image
The application UI freezes.
The app doesn't crash, as near as I can tell, the UI simply becomes unresponsive. This is both in the simulator and on the device.
Here is how I use the initialized property with my cell:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = self.checkmarkOffAccessoryView;
else
cell.accessoryView = nil;
}
With the aforementioned code, only one cell shows the accessory view and the UI freezes.
If I initialize the UIImageView instance directly in the delegate method I get all condition-satisfying cells showing the accessory view and I do not experience the UI freeze:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
else
cell.accessoryView = nil;
}
My goal is to initialize as few objects as possible and reuse one UIImageView. I'm curious why the first chunk of code is problematic and what I could do to fix this.
It seems like the cell's accessoryView property should just increment the retain count of self.checkmarkOffAccessoryView but it appears I am missing some detail.
What have I overlooked? Thanks for your advice.
EDIT
I think that:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
is the same as:
UIImageView *uncheckedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]];
self.checkmarkOffAccessoryView = uncheckedView;
[uncheckedView release];
Either way, I experience the same freeze symptom.
You cannot add the same view multiple times. The UI handler will go bonkers. To make sure of this, I tried doing what you said above and I got the same issue. The UI freezes up, the image only appears for one of the cells.
The best thing you can do is to store your image as a UIImage allocated, and to have a helper function which returns a new UIImageView per cell.
Using your current method (without a stored UIImage) you might do:
-(UIImageView *) makeCheckmarkOffAccessoryView
{
return [[[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
}
And then do
cell.accessoryView = [self makeCheckmarkOffAccessoryView];
As you may be aware, UIImages on the other hand may be used any number of times. a UIImageView doesn't take up a lot of space, so you can easily have a bunch of those without worrying.
To expand on the one place only deal, imagine that you add a UIView to two places at the same time.
What will [ob removeFromSuperview] do for this object? Will it remove the view from both places? From one of them only? Which value will be returned when you request [ob superview]? Clearly the UI is not made to handle what you're asking for.
Try it without the autorelease in the initializer. I suspect you're over-releasing.
By the way, your console probably is showing a BAD_ACCESS error when it freezes. If you turn on NSZombieEnabled, my guess is you'll see it's making a call to a deallocated UIImage.
maybe this will help
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ShoppingListCell";
HSShoppingListCell *cell = (HSShoppingListCell *)[aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ShoppingListCell"
owner:self
options:nil];
cell = shoppingListCell;
}
ShoppingListItem *theItem = nil;
theItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *selected = [UIImage imageNamed:#"listBullet_checked.png"];
UIImage *notSelected = [UIImage imageNamed:#"listBullet.png"];
cell.imageView.image = ([theItem.checkedOff boolValue] ? selected : notSelected);
cell.shoppingListLabel.text = theItem.productName;
[cell.shoppingListLabel setFont:[UIFont fontWithName:#"Marker Felt" size:26.0]];
return cell;
}
- (void)toggleCellImage:(NSIndexPath *)indexPath
{
ShoppingListItem *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
item.checkedOff = ([item.checkedOff boolValue] ? [NSNumber numberWithBool:NO] : [NSNumber numberWithBool:YES]);
[HSCoreDataUtilities saveContext:item.managedObjectContext];
[self.tableView reloadData];
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self toggleCellImage:indexPath];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Reducing your case to the bare essentials (I was going to suggest to put two 'thin' UIView objects around the UIImageView...), I found that it is most probably impossible.
Create 2 empty UIView objects in IB, hook them up to bareView1 and bareView2. Then
UIImageView *imageView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"test.png"]];
[bareView1 addSubview:imageView]; // it shows either here ...
[bareView2 addSubview:imageView]; // ... or here
You can never get the image on sceen more than once like this. As a rule of thumb, I think the first object in line which does not inherit from UIView can be used multiple times, i.e. the UIImage. Like Kalle stated, a UIView can only have one parent in the view hierarchy.
Postponing the second addSubview only makes the UIImageView jump from bareView1 to bareView2.
The freeze happens maybe because the event handling gets mixed up: the accessory can be interactive, how would you know which one was tapped if they are one and the same object? So the code assumes objects are unique, and you manage to violate that assumption.

Cocoa-Touch: How to: custom UITableViewCell with UIColor clearColor background

I have a UITableView with custom cells.
I have MyTableViewCell : UITableViewCell and MyTableViewCellContentView : UIView classes.
What I'm doing is basically what is done in the AdvancedTableViewCells demo app from Apple with a slight change, on some rows I want to use a clearColor background to show the table's background behind the painted text.
So in MyTableView's cellForRowAtIndexPath I'm doing:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"MyCell";
MyTableViewCell *cell = (MyTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.someValue = indexPath.section;
[cell finishedSetup];
return cell;
}
And in my MyTableViewCell's finishedSetup:
cellContentView = [[MyTableViewCellContentView alloc] initWithFrame:CGRectMake(0, 0, 320, 80) cell:self];
cellContentView.autoresizingMask = UIViewAutoresizingNone;
cellContentView.contentMode = UIViewContentModeRedraw;
[self.contentView addSubview:cellContentView];
And in MyTableViewCellContentView I implement the drawRect method. And plan to not use any subviews but draw my custom content just as the Apple example does in the CompositeSubviewBasedApplication.
The problem is that for a few sections I want to use a clearColor backgroundColor. This works, until a cell with a non-clearColor backgroundColor is reused to draw a clearColor cell, at which time the background is not cleared and will still have the old color.
How can I make the background redraw?
Edit:
I forgot to mention, I'm setting the background color in MyTableViewCellContentView's init after calling super's init. Setting it via:
self.backgroundColor = [UIColor clearColor];
I've verified that this in fact does get called and is called as expected with clearColor or redColor.
I've also tried setting the table cell's background color, it didn't help.
Edit #2: Here's my drawRect method:
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
static int i = 0;
[[NSString stringWithFormat:#"Cell %d", ++i] drawAtPoint:CGPointMake(3, 3) withFont:[UIFont boldSystemFontOfSize:16]];
}
To make the background color setting take effect you need to do the setting in tableView:willDisplayCell:forRowAtIndexPath - the OS will not alter anything you set here. The reason is that some additional setup of the cell gets done by the OS after you return it from tableView:cellForRowAtIndexPath. If you can, get a look at session 101 from WWDC 09 presented by Jason Beaver.
I haven't found out why it was happening. But I've tried a different strategy. I'm no longer reusing table cells, since I don't have that many of them.
Instead I'm creating all cells at startup and actually get a boost in performance when showing them since this way there is no additional setup needed when scrolling.