I am trying to add a CALayer to the bottom part of my UITableViewCell to get a bit of a "shadow" effect. The problem is when the table is scrolled up and off the screen the layer is removed, so when you scroll back down it isn't visible. If you scroll the cells downward off the screen then back up they appear fine.
I have a gif here showing what's happening.
This is how I'm doing it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:@"Row %d", (int)indexPath.row];
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0.0f, cell.frame.size.height, cell.frame.size.width, 1.0f);
bottomBorder.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f].CGColor;
[cell.layer addSublayer:bottomBorder];
cell.clipsToBounds = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
Which I've also tried using a unique cellIdentifier for each cell in hopes that they wouldn't be reused and thus the layer never removed like this:
//Same code as before just this changed
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:@"%ld_%ld", (long)indexPath.row, (long)indexPath.section]];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[NSString stringWithFormat:@"%ld_%ld", (long)indexPath.row, (long)indexPath.section]];
}
//Same code continued...
So my problem is that when a cell is reused, the CALayer added to it is removed. What do I need to do to keep that layer there when the cell is scrolled off the screen and back on.
EDIT:
I've also tried this which doesn't work either:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0.0f, cell.frame.size.height, cell.frame.size.width, 1.0f);
bottomBorder.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f].CGColor;
[cell.layer addSublayer:bottomBorder];
cell.clipsToBounds = NO;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.textLabel.text = [NSString stringWithFormat:@"Row %d", (int)indexPath.row];
return cell;
}
GACK! Do NOT use a different cell identifier for every cell. That totally defeats the cell reuse mechanism, and will give you both ever-increasing memory use AND force you to always create a new cell for every index - a worst possible outcome.
Every time a cell scrolls off-screen, it goes into the reuse queue. Then when a new index path scrolls into view, the system takes a previously-used cell out of the recycle bin and hands it to you. You should assume that it's STRUCTURE is fully formed (any custom fields you've added will be there) but that the contents are all wrong, and need to be fully set. So if you have a cell with 3 labels, and some objects in your model fill in text into 1 label, some into 2, and some into all 3 labels, you should ALWAYS set a value to all 3 labels, even if it's an empty string. That prevents values left over from the last use of the cell from sticking around.
It's kind of like a doctor's office that has patient forms for you to fill out. You fill out a form and give it to the clerk. The clerk keys the info into a computer and then puts the form back on top of the blank pile of forms. The next patient has to erase EVERYTHING off the form, including names of kids if they don't have any kids, spouse, pre-existing conditions they don't have, etc, etc. Otherwise the info from the last person to use the form will still be on your form.
As for your shadow layer, when you fetch a cell using dequeueReusableCellWithIdentifier, you should only add your layer if you have to create a new cell. If you get back a reused cell, the layer will already be there, but you might need to set it's contents if they change from cell to cell.
It turns out the reason why it was being removed was because the CALayer was being added to the cell rather than to the UITableViewCell contentView. So the proper implementation is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
CALayer *bottomBorder = [CALayer layer];
bottomBorder.frame = CGRectMake(0.0f,
(CGRectGetHeight(cell.contentView.bounds) - 1),
CGRectGetWidth(cell.contentView.bounds),
1.0f);
bottomBorder.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f].CGColor;
[cell.contentView.layer addSublayer:bottomBorder];
}
cell.textLabel.text = [NSString stringWithFormat:@"Row %d", (int)indexPath.row];
return cell;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With