Why is drawRect leaving parts of image?

ぐ巨炮叔叔 提交于 2020-01-23 12:59:08

问题


Here's my case:

I have a table which contains a list of restaurants, each entry shows the results of inspections over time on a scorebar that is drawn using drawRect.

When the user scrolls the table down and then up, so that scorebars with yellow and red squares are shown, previous scorebars pick up those squares. Dates are also drawn into previous scorebars.

My problem lies in getting rid if the old squares. Shouldn't they be erased each time drawRect is called?

I've placed three images below that hopefully explain what I mean.

I don't think this is a problem with table rows caching custom cells? Here the scorebars are drawn once the UITableCell establishment cell is dequeued; all the other views in the cell are correct, which makes me think the issue is with drawRect failing to clear the graphics context.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  if (indexPath.row < self.establishments.count) {
      return [self establishmentCellForIndexPath:indexPath];
  } else if (self._noResultsFound) {
      return [self noResultsCell];
  } else {
      return [self loadingCell];
  }
}

- (UITableViewCell *)establishmentCellForIndexPath:(NSIndexPath *)indexPath {

  static NSString *CellIdentifier = @"EstablishmentCell";

  DSFEstablishment *establishment = [self.establishments objectAtIndex:[indexPath row]];

  DSFEstablishmentCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

  [cell setEstablishment:establishment];
  [cell updateCellContent];

  return cell;
}

During updateCellContent, we try to clear out old scorebarviews, if they exist, but the code never finds additional subviews of the DSFScorebarView class. Finally, the scorebarView is created and added to the view's subviews.

- (void)updateCellContent {
NSLog(@"updateCellContent: %@", self.establishment.latestName);

self.name.text = self.establishment.latestName;
self.address.text = self.establishment.address;
self.type.text = self.establishment.latestType;
if (self.establishment.distance) {
    self.distance.text = [NSString stringWithFormat:@"%.2f km", self.establishment.distance];
} else {
    self.distance.text = @"";
}

// Clear out old scorebarView if exists
for (UIView *view in self.subviews) {
    if ([view isKindOfClass:[DSFScorebarView class]]) {
        NSLog(@">>>removeFromSuperview");
        [view removeFromSuperview];
    }
}

DSFScorebarView *scorebarView = [[DSFScorebarView alloc] initWithInspections:self.establishment.inspections];
[self addSubview:scorebarView];

}

The scorebarview is drawn using drawRect within initWithInspections and ensures that scorebars are no more than 17 squares long (most recent inspections). Each inspection is given a square of green = low|yellow = moderate|red = high and the year is drawn below the square.

I've set self.clearsContextBeforeDrawing = YES, but it doesn't correct the problem.

// Custom init method
- (id)initWithInspections:(NSArray *)inspections {
CGRect scorebarFrame = CGRectMake(20, 38, 273, 22);
if ((self = [super initWithFrame:scorebarFrame])) {
    self.inspections = inspections;

    self.clearsContextBeforeDrawing = YES;

    [self setBackgroundColor:[UIColor clearColor]];
}
return self;
}

- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();

float xOffset = 0; // Keep track of position of next box -- start at 1
float yOffset = 0; // Fixed Y offset. Adjust to match shadows
NSString *previousYear = nil;
UIFont *yearFont = [UIFont fontWithName:@"PFTempestaFiveCompressed" size:8.0];

// take subset/slice of inspections. only the last 17, so it will fit on screen.
int inspections_count = (int)[self.inspections count];

int startIndex = inspections_count > 17 ? inspections_count - 17 - 1 : 0;
int subarrayLength = inspections_count > 17 ? 17 : inspections_count;
NSArray *inspectionsSlice = [self.inspections subarrayWithRange:NSMakeRange(startIndex, subarrayLength)];

for (id inspection in inspectionsSlice) {

    // Top of box
    NSMutableArray *pos0RGBA = [inspection colorForStatusAtPositionRGBA:0];
    CGFloat topColor[] = ...
    CGContextSetFillColor(ctx, topColor);
    CGRect box_top = CGRectMake(xOffset, yOffset, kScoreBoxWidth, kScoreBoxHeight / 2);
    CGContextAddRect(ctx, box_top);
    CGContextFillPath(ctx);

    // Bottom of box
    NSMutableArray *pos1RGBA = [inspection colorForStatusAtPositionRGBA:1];
    CGFloat bottomColor[] = ...
    CGContextSetFillColor(ctx, bottomColor);
    CGRect box_bottom = CGRectMake(xOffset, kScoreBoxHeight / 2 + yOffset, kScoreBoxWidth, kScoreBoxHeight / 2);
    CGContextAddRect(ctx, box_bottom);
    CGContextFillPath(ctx);

    // Year Text
    NSString *theYear = [inspection dateYear];
    if (![theYear isEqualToString:previousYear]) {
        CGFloat *yearColor = ...
        CGContextSetFillColor(ctx, yearColor);
        // +/- 1/2 adjustments are positioning tweaks
        CGRect yearRect = CGRectMake(xOffset+1, yOffset+kScoreBoxHeight-2, kScoreBoxWidth+50, kScoreBoxHeight);
        [theYear drawInRect:yearRect withFont:yearFont];
        previousYear = theYear;
    }

    xOffset += kScoreBoxWidth + kScoreBoxGap;
}
  • Table loads, 4 rows are drawn

  • User scrolls down; 8 more rows are drawn

  • User scrolls back to the top; the scorebar for Grace Market/Maple Pizza (row 2) has changed.

After implementing @HaIR's solution below, I get a white strip against the grey background, after tapping a table row. It's pretty ugly, after adding the extra width to 'white-out' the Year line.

- (void)drawRect:(CGRect)rect {
  ...
  CGFloat backgroundColour[] = {1.0, 1.0, 1.0, 1.0};
  CGContextSetFillColor(ctx, backgroundColour);
  CGRect fullBox = CGRectMake(xOffset, yOffset, kScoreBoxWidth * 17, 2 * (kScoreBoxHeight-2));
  CGContextAddRect(ctx, fullBox);
  CGContextFillPath(ctx);

  for (id inspection in inspectionsSlice) {
  ...


回答1:


You've taken over DrawRect, so its only going to do what you manually do in the dra

Fill in the entire bar area with background color before you draw anything on top of it.

You are re-using cells, so, for example, you have the Turf Hotel Restaurant graph showing underneath the 2nd version of the Grace Market/Maple Plaza.

CGContextSetFillColor(ctx, yourBackgroundColor);
CGRect fullBox = CGRectMake(xOffset, yOffset, kScoreBoxWidth * 17, kScoreBoxHeight);
CGContextAddRect(ctx, fullBox);
CGContextFillPath(ctx);

If you are just clearing the background in your cells instead of having a background color. then:

CGContextClearRect(context, rect);

at the start of your DrawRect may be more appropriate.

REVISION:

Looking at your question more closely, the root problem is that you are not finding and deleting the old DSFScoreboardView's. Perhaps you should try a recursive search through the subviews. Like:

- (void)logViewHierarchy {
    NSLog(@"%@", self);
    for (UIView *subview in self.subviews) {
        //look right here for your DSFScorebarView's
        [subview logViewHierarchy];
    }
}
@end

// In your implementation
[myView logViewHierarchy];

(taken from https://stackoverflow.com/a/2746508/793607)



来源:https://stackoverflow.com/questions/25478319/why-is-drawrect-leaving-parts-of-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!