max_
max_

Reputation: 24481

iOS - DrawRect performance issue

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];
}

Upvotes: 0

Views: 2178

Answers (3)

Nicolas Miari
Nicolas Miari

Reputation: 16246

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...

Upvotes: 1

DarthMike
DarthMike

Reputation: 3481

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.

Upvotes: 1

justin
justin

Reputation: 104698

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.

Upvotes: 2

Related Questions