Search code examples
ioscocoa-touchcore-text

Keep a reference to a CTFramesetter to speed up Core Text drawing


I wish to render text on a large amount of UITableViewCell's. The text is rendered in a UITableViewCells drawRect:(CGRect)rect method.

Each cell contains a different number of lines and the text has attributes like weight and color.

In - (void) tableview:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath; I pass the tableViewCell the attributedString, which I pre made when building my model. I attached some code that shows the gist of what goes on in my drawRect.

I keep reading (documentation, blogs etc.) that the CTFrameSetter is the most expensive object to create. This makes sense as this is were all the calculations for the glyphs and runs are calculated. My question now is, can I pre make this as well and pass it to my cell on cellForRowAtIndexPath and is it likely to have a positive effect on speed?

- (void) drawRect:(CGRect)rect
{   
/* These are pre-populated and the attributedString is passed to the cell at run time

    NSDictionary *stringAttributeDict = [NSDictionary dictionaryWithObjectsAndKeys:
                         (id)fontRef, (NSString*)kCTFontAttributeName, 
                         (id)[[UIColor blueColor] CGColor], (NSString*)(kCTForegroundColorAttributeName), nil]];
    NSDictionary *firstPartAttributeDict = [NSDictionary dictionaryWithObjectsAndKeys:
                          (id)fontRef, (NSString*)kCTFontAttributeName, 
                          (id)[[UIColor greenColor] CGColor], (NSString*)(kCTForegroundColorAttributeName), nil]];

    [attributedString addAttributes:stringAttributeDict range:NSMakeRange(0, [attributedString.string length])];
    [attributedString addAttributes:firstPartAttributeDict range:NSMakeRange(0, 5)];

*/
    rect = CGRectInset(rect, 5.0, 5.0);

    CGContextRef context = UIGraphicsGetCurrentContext();

    // Flip the coordinate system
    CGContextSetTextMatrix(context, CGAffineTransformIdentity);
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    //Make a path to render the text in
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, rect);

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CTFrameRef theFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [attributedString length]), path, NULL);
    CFRelease(framesetter);
    CFRelease(path);

    //Draw the text:
    CTFrameDraw(theFrame, context);
    CFRelease(theFrame);
}

Solution

  • Yes, you should be able to keep it around and share it between cells. I have kept a persistent CTFramesetterRef around for multiple text blocks inside of a single view, I can't see why using it for multiple cells in a tableview would be any different.