0

I am making a Universal app for the iPhone/iPad and it seems that there is a lot of lag with the following code:

CALayer *imageLayer = [cell.myImageView layer];
[imageLayer setMasksToBounds:YES];
[imageLayer setCornerRadius:6.0];

What is odd is that the iPhone version has no lag while the iPad version has some lag. I do no differentiation in code between the two devices but if I comment out that code above everything seems to be fine on both devices. However, I still want rounded corners on my UIImageView, "myImageView".

I do that bit of code in the cellForRowAtIndexPath only during the first cell initialization since all of my cells are the same.

Does anyone know why this is happening?

Thanks!

SimplyKiwi
  • 12,376
  • 22
  • 105
  • 191

1 Answers1

1

CALayer operations are using a lot of cpu power to draw.

So the best solution would be to draw the image using core graphics. Loren Brichter was the first to show us how:

This is the code for his ABTableViewCell header (.h) file

// Copyright (c) 2008 Loren Brichter
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
//  ABTableViewCell.h
//
//  Created by Loren Brichter
//  Copyright 2008 Loren Brichter. All rights reserved.
//

#import <UIKit/UIKit.h>

// to use: subclass ABTableViewCell and implement -drawContentView:

@interface ABTableViewCell : UITableViewCell
{
    UIView *contentView;
}

- (void)drawContentView:(CGRect)r; // subclasses should implement

@end

And here is the code for the impelementation (.m) file:

// Copyright (c) 2008 Loren Brichter
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
//  ABTableViewCell.m
//
//  Created by Loren Brichter
//  Copyright 2008 Loren Brichter. All rights reserved.
// 

#import "ABTableViewCell.h"

@interface ABTableViewCellView : UIView
@end

@implementation ABTableViewCellView

- (void)drawRect:(CGRect)r
{
    [(ABTableViewCell *)[self superview] drawContentView:r];
}

@end



@implementation ABTableViewCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
        contentView = [[ABTableViewCellView alloc]initWithFrame:CGRectZero];
        contentView.opaque = YES;
        [self addSubview:contentView];
    }
    return self;
}


- (void)setFrame:(CGRect)f
{
    [super setFrame:f];
    CGRect b = [self bounds];
    b.size.height -= 1; // leave room for the seperator line
    [contentView setFrame:b];
}

- (void)setNeedsDisplay
{
    [super setNeedsDisplay];
    [contentView setNeedsDisplay];
}

- (void)drawContentView:(CGRect)r
{
    // subclasses should implement this
}

@end

Now what you need to do is draw your cell in the drawContentView method using Core Graphics.

Here is a part of the code I use in the App I'm working on:

    CALayer *cellLayer = [[CALayer alloc] init];
    CGRect cellFrame = self.bounds;
    CGRect layerFrame = CGRectInset(cellFrame, 3, 3);

    cellLayer.frame = cellFrame;
    cellLayer.contentsScale = [[UIScreen mainScreen] scale];

     //round corners in Core Graphics (much faster than using the CALayer cornerRadius)
     UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [[UIScreen mainScreen] scale]);

     CGContextRef imgContext = UIGraphicsGetCurrentContext();

    CGContextSaveGState(imgContext);  
    //create the rounded rectangle to draw the image in
    CGPathRef clippingPath = [UIBezierPath bezierPathWithRoundedRect:layerFrame cornerRadius:6.0f].CGPath;
    CGContextAddPath(imgContext, clippingPath);
    CGContextClip(imgContext);

CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)[NSData dataWithContentsOfFile:ImageName]);
            CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);
            CGDataProviderRelease(imgDataProvider);
            CGContextDrawImage (imgContext, layerFrame, imageRef);
            CGImageRelease(imageRef);

CGContextRestoreGState(imgContext);

        //draw white outline
        CGPathRef path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(cellFrame, 3, 3) cornerRadius:6.0f].CGPath;
        CGContextAddPath(imgContext, path);
        CGContextSetLineWidth(imgContext, 2.0);
        CGContextSetRGBStrokeColor(imgContext,1,1,1,1);
        CGContextStrokePath(imgContext);

        // Get the image from the context
        UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();


        cellLayer.contents = (__bridge id)img.CGImage;

To use the custom cell you need to initialize it in your tableView CellForRowAtIndexPath datasource method this way:

    static NSString *CellIdentifier = @"TableCell";

    ABTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {        
        cell = [[ABTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
}

Also remember that since you are doing custom drawing you need to ALWAYS reresfh the cell view when you modify something your self by using:

[cell setNeedsDisplay];
Lefteris
  • 14,550
  • 2
  • 56
  • 95
  • Hmm, no that didn't help that much. Still a lot of lag. Even when I present my Game Center modal view in this view, the animation to present that achievement view has lag. No other view in my app has this issue and it is really odd. What else could I optimize? – SimplyKiwi Jun 02 '12 at 20:42
  • If you don't set the cell Image, how much FPS are you measuring? Just want to see where the problem is... I would remove the whole profile picture section and measure – Lefteris Jun 02 '12 at 20:56
  • Take a look at my whole question and original post again, I changed everything :P Also if you could be kind enough and get rid of the code in your answer, that would be great since I would rather not have the code visible that easily since it will eventually be in my app! :) – SimplyKiwi Jun 02 '12 at 20:59
  • Thank you. Any idea why the CALayer is causing all the lag? – SimplyKiwi Jun 02 '12 at 21:05
  • Wow is all that code really necessary just for getting rounded corners on an UIImageView in a UITableViewCell? – SimplyKiwi Jun 02 '12 at 21:42
  • Would it use less CPU if I used rounded UIImage's and fed them into a regular rectangular UIImageView? – SimplyKiwi Jun 02 '12 at 21:50
  • Well I showed the fastest approach. Sure if your images are already rounded, it will solve your problem. ALso here is a link that might work for you. It suggests subclassing the UIIMageView and create a rounded rect UIImageView: http://stackoverflow.com/a/2478839/312312 – Lefteris Jun 02 '12 at 22:09
  • Alright thank you. Im sure one of these 3 approaches shall work! :) – SimplyKiwi Jun 02 '12 at 22:22
  • Sorry, last question. With your class above. How would I properly call it? – SimplyKiwi Jun 02 '12 at 22:49
  • I've updated the answer. Also there is a great example in Brad Larsons & Mugunth Kumar's book "iOS 5 Programming Pushing the Limits". http://blog.mugunthkumar.com/products/introducing-my-book-ios-5-programming-pushing-the-limits/ They explain how to use this approach very well – Lefteris Jun 02 '12 at 22:58