Kunal Kumar Kunal Kumar - 5 months ago 78
Objective-C Question

tableView cell dotted border not rendering properly

Consider me novice in iOS programing. I am trying to show dotted border as the separator as well as a vertical dotted border between the fields, please check the screenshot for detailed view. Till now there is no problem, borders are rendering properly.But at the moment tableView get scrolled up or down, the position of vertical dotted lines are getting changed, please check screenshot. I know this is happening due to the

cell reuse
property. For all the cell dotted line creating method is same so, I am unable to find the
culprit line of code
. I am using
ViewController
and attached an
UITableView
to it, connected the datasource and delegates. Here is the code for dotted line creation.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
KKCustList *cell = [tableView dequeueReusableCellWithIdentifier:@"custList" forIndexPath:indexPath];

if (cell==nil)
{
cell = [[KKCustList alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"custList"];
}
/** Creating dotted lines for border **/


CAShapeLayer *shapelayer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(0, cell.frame.size.height)];
[path addLineToPoint:CGPointMake(cell.frame.size.width, cell.frame.size.height)];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = [UIColor firstRowColor].CGColor;
shapelayer.lineWidth = 2.0;
shapelayer.lineJoin = kCALineJoinRound;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2 ], nil];
shapelayer.path = path.CGPath;
[cell.layer addSublayer:shapelayer];

/** creating vertical dotted line **/

CAShapeLayer *shapelayer1 = [CAShapeLayer layer];
UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(cell.soLabel.bounds.origin.x + cell.soLabel.bounds.size.width, cell.soLabel.bounds.origin.y + 10)];
[path1 addLineToPoint:CGPointMake(cell.soLabel.bounds.origin.x + cell.soLabel.bounds.size.width, cell.soLabel.bounds.size.height)];
shapelayer1.strokeStart = 0.0;
shapelayer1.strokeColor = [UIColor firstRowColor].CGColor;
shapelayer1.lineWidth = 1.0;
shapelayer1.lineJoin = kCALineJoinRound;
shapelayer1.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2 ], nil];
shapelayer1.path = path1.CGPath;
[cell.layer addSublayer:shapelayer1];


Desired

after scrolling

UPDATE
moved the code to awakeFromNib

@implementation KKCustList
@synthesize shapelayer1;

- (void)awakeFromNib {

[super awakeFromNib];

/** creating vertical dotted line **/

CAShapeLayer *shapelayer1 = [CAShapeLayer layer];
UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(self.soLabel.bounds.origin.x + self.soLabel.bounds.size.width, self.soLabel.bounds.origin.y + 10)];
[path1 addLineToPoint:CGPointMake(self.soLabel.bounds.origin.x + self.soLabel.bounds.size.width, self.soLabel.bounds.size.height)];
shapelayer1.strokeStart = 0.0;
shapelayer1.strokeColor = [UIColor firstRowColor].CGColor;
shapelayer1.lineWidth = 1.0;
shapelayer1.lineJoin = kCALineJoinRound;
shapelayer1.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2 ], nil];
shapelayer1.path = path1.CGPath;
[self.contentView.layer addSublayer:shapelayer1];

}

Answer

Your current code has a couple of issues:

  1. You keep adding new layers, you never remove old ones, so each time a cell is reused it'll take more memory and the lines could be in the wrong place
  2. You never move the sublayers when the cell size changes

So you need a couple of different fixes:

  1. Add the lines in your cell in awakeFromNib, so they're just added once in the same way that all your other views are
  2. Override layoutSubviews in your cell and move the layers you've added each time it's called so they don't overlap other views

I'd actually drop the sublayers approach probably and use an image view for the line. If the lines might change quite a lot then I'd likely use a custom view class which has a sublayer (as per your current code) and the resizing / layout logic as described above. The reason for that is so that you can apply auto-layout constraints to the view and it will move in relation to the cell and your other views automatically.

You should also remove

if (cell==nil)
{
    cell = [[KKCustList alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"custList"];
}

because it will never be used, a cell will always be created for you.