0

I am using -drawRect for the first time in an attempt to alternatively speed up a UITableView. However, the drawRect method seems to be slowing the table down quite largely.

Please can you tell me how I can improve the drawRect method below in order to speed up the table?

Edit---

In the drawRect method, I am writing two NSStrings to the cell's view, two UIImages and a drop shadow to both of the NSStrings and one of the UIImages.

One of the aforementioned images is downloaded asynchronously and then setNeedsDisplay is called to draw that UIImage to the screen. I believe that this could initially be the reason for the lag occurring.

- (void) drawRect:(CGRect) rect {

    CGContextRef context = UIGraphicsGetCurrentContext();

    [[UIColor clearColor] set];

    CGContextFillRect(context, rect);

    CGContextSaveGState(context);
    CGContextSetShadow(context, CGSizeMake(1,1),1);
    //draw text here

    if (shouldDrawImage == YES) {

        CGContextDrawImage(context, CGRectMake(10, 10, 40, 40), self.image.CGImage);

    }

    CGContextDrawImage(context, CGRectMake(self.frame.size.width - 16, 0, 16, self.frame.size.height), [UIImage imageNamed:@"right_bar_including_holes"].CGImage);

    NSString *authorName = [[self.info objectForKey:@"user"] objectForKey:@"full_name"];

    [RGB(219, 240, 73) set];

    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width - 10 - 55 - 16,9999);
    CGSize authorsize = [authorName sizeWithFont:[UIFont boldSystemFontOfSize:15]
                                     constrainedToSize:maximumLabelSize 
                                         lineBreakMode:UILineBreakModeWordWrap]; 
    [authorName drawInRect:CGRectMake(60, 10, self.frame.size.width - 60, authorsize.height) withFont:[UIFont boldSystemFontOfSize:15]];

    [RGB(249,249,249) set];

    NSString *description = [self.info objectForKey:@"description"];
    CGSize descriptionSize = [description sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap];

    [description drawInRect:CGRectMake(60, authorsize.height + 15, descriptionSize.width, descriptionSize.height) withFont:[UIFont systemFontOfSize:14]];

    CGContextRestoreGState(context);

}

- (NSString *) reuseIdentifier {
    return NSStringFromClass([SlideCell class]);
}

- (void) updateCellInfo:(NSDictionary *)_info {
    [self setInfo:_info];

    UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
    [iv setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[self.info objectForKey:@"user"] objectForKey:@"avatar"]]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *_image) {
        dispatch_async(dispatch_get_main_queue(), ^{
            shouldDrawImage = YES;
            self.image = [self roundedImage:_image];
            [iv release];
            [self setNeedsDisplay];
        });

    } failure:nil];

    [self setNeedsDisplay];

    [self setSelectionStyle:UITableViewCellSelectionStyleNone];
}
max_
  • 24,076
  • 39
  • 122
  • 211
  • 1
    It would help your question if you explained WHY you are using drawRect and what SPECIFICALLY you are doing with it rather than expect someone to wade through your code to see what issues there may be. – spring Jun 15 '12 at 21:35
  • 1
    Overriding drawRect to improve performance seems misguided to me. You are making an assumption that you write more efficient code then the guys that wrote UITableView. I'm willing to bet there is an easier and more effective solution out there. – sosborn Jun 15 '12 at 21:45

3 Answers3

2

Yes - you run Instruments' Time Profile on your app to tell you exactly how much time is spent, and where. It will tell you if it is the image, string, shadow, or something else.

justin
  • 104,054
  • 14
  • 179
  • 226
1

You should profile that code and see if image is the problem first.

I'm not sure how AFNetworking library (which you are using) works when downloading asynchronously the images.

If you know that image is the problem, I suspect that image needs to be rescaled in UIImageView when it's set. That could be the problem. You would need to rescale the UIImage you want to set to the UIImageView to the UIIImageView's frame so no autorescaling triggers. That's costly when scrolling.

You receive image and inmediately dispatch code to main thread. Potential rescaling could work 'under the hood'. I would change that method to:

UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, IMGSIZEX, IMGSIZEY)];
    [iv setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[self.info objectForKey:@"user"] objectForKey:@"avatar"]]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *_image) {
        //Note you have to implement rescaleToFitSize
        UIImage* rescaled = [_image rescaleToFitSize:iv.frame.size];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.image = _image;
            [iv release];
            [self setNeedsDisplay];
        });

    } failure:^{
         //Handle failure! You create mem. leak on failure
         [iv release];
}];

As a side note, you don't handle failures in image download. You definitively should.

DarthMike
  • 3,471
  • 1
  • 22
  • 18
  • Agreed. You can start with a placeholder empy image and update it once the asynchronous download completes (if the cell in question still needs THAT image). – Nicolas Miari Jun 16 '12 at 07:28
1

Stock UITableView is as efficient as it gets, provided you use it properly: 1. Recycle cells (dequeue) 2. Avoid transparency whenever posible (alpha blending DOES slow things down) 3. Configuration of reused cells doesn't take much processing time.

I don't think anyone can significantly improve on Apple's code by overriding drawRect...

Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
  • 1
    If you have a lot of subviews in your cell, scrolling performance can suffer. Having a single custom content view and drawing it all manually can give improved performance. – jrturton Jun 16 '12 at 08:34
  • 1
    Yes, but making the views opaque helps a lot (e.g. Setting label background colors to match that of the content view, instead of clearColor). In any case, you should override the cell's drawRect, not the table's. – Nicolas Miari Jun 16 '12 at 09:53
  • 1
    Fully agree, and I think the questioner _is_ overriding a cell, not the table, it just isn't clear. – jrturton Jun 16 '12 at 10:40
  • Even if you stick to UIKit, it helps a lot to consider that under the hood it's all OpenGL ES. All iOS device GPUs are fill rate-bound (overdrawing is very costly). The less blending, the more the driver can use hidden surface removal and other optimizations. – Nicolas Miari Jun 16 '12 at 11:13