0

In my app I have a UIImageView in a UITableViewCell. The images need to be displayed as circular images with rectangular source images. I have been setting the corner radius on the imageView's layer property, but this forces an offscreen render which is slowing performance on scrolling.

Here's what I currently have in the awakeFromNib method on the cells:

- (void)awakeFromNib
{
    [super awakeFromNib];

    self.imageBorderView.backgroundColor = [UIColor colorWithWhite:0.75f alpha:1.f];
    self.imageBorderView.layer.cornerRadius = self.imageBorderView.frame.size.width / 2;
}

Really basic stuff. Ok, so I just watched the 2011 WWDC Session 121 video, Understanding UIKit Rendering. That covered this issue as it relates to a UILabel by moving the corner radius rendering to drawRect using UIBezierPaths. I subclassed UIImageView and moved my cornerRadius drawing to drawRect only to find that drawRect is not called on a UIImageView. Here's the relevant reasoning from the docs:

The UIImageView class is optimized to draw its images to the display. UIImageView does not call the drawRect: method of its subclasses. If your subclass needs to include custom drawing code, you should subclass the UIView class instead.

So I changed the superclass to UIView and I am manually drawing a circle using UIBezierPath and the UIImage in drawRect. Here is my drawRect method:

- (void)drawRect:(CGRect)rect
{
    UIBezierPath *circularPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.height / 2.0];
    [[UIColor redColor] set];
    [circularPath fill];

    if (self.image)
    {
        [self.image drawInRect:self.bounds];
    }
}

It does exactly what I would expect, it draws the red circle (just a sanity check to make sure the path is correct), then it draws the rectangular image over the circle if one has been set.

What I can't figure out is how to crop the UIImage to the circular UIBezierPath without drawing offscreen. This means setting an object as the mask wont work.

Is there a way to mask an image to a path without rendering offscreen?

Sean Kladek
  • 4,396
  • 1
  • 23
  • 29

2 Answers2

2

Call -[UIBezierPath addClip] to clip subsequent drawing to the inside of the path.

- (void)drawRect:(CGRect)rect
{
    UIBezierPath *circularPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:self.bounds.size.height / 2.0];
    [circularPath addClip];
    [self.image drawInRect:self.bounds];
}
Kurt Revis
  • 27,695
  • 5
  • 68
  • 74
  • Perfect. Images follow the bezier path and rendering occurs on screen. Will have to wait until tomorrow to test on device, but according to the profiling, this has saved significant time. – Sean Kladek Oct 27 '14 at 04:41
0

You are overcomplicating things, some years ago I had the same problem and the way I solved it was using a programmatically created UITableViewCell which had a UIView in which I used QuartzCore and set the cornerRadius, afterwards I added a UIImageView inside of it (the UIView) and it always rendered it super fast without going into much detail of UIBeziers.

MrJomp
  • 135
  • 6
  • That was my initial approach, but it is not rendering fast enough. It's not terrible, but it's not buttery smooth like it should be. – Sean Kladek Oct 27 '14 at 04:37
  • If that is your answer then I need to ask/suggest you two things, first how big are your images (in size and dimensions) have you debugged your app using **Xcode's Instruments** and see the memory and CPU usage,cause it might me happening that most of the time the memory is being bloated with images in and out because of reusable cells or the cpu processing **big** images. – MrJomp Oct 27 '14 at 04:57