Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView overriding drawRect causes view not to obey masksToBounds

I am trying to override the drawRect: method of UIView in my custom view. However, my view has a border radius defined as:

    sub = [[[NSBundle mainBundle] loadNibNamed:@"ProfileView" owner:self options:nil] objectAtIndex:0];
    [self addSubview:sub];
    [sub setUserInteractionEnabled:YES];
    [self setUserInteractionEnabled:YES];
    CALayer *layer = sub.layer;
    layer.masksToBounds = YES;
    layer.borderWidth = 5.0;
    layer.borderColor = [UIColor whiteColor].CGColor;
    layer.cornerRadius = 30.0;

This works perfectly and places a nice border with a border radius around my view (don't mind the diagonal/straight white lines at the back, they have nothing to do with this view):

correct one

However, when I try to override the drawRect: method in my view, I can see a black background not masking to bounds. I don't do anything (currently), here is my code:

-(void)drawRect:(CGRect)rect{
    [super drawRect:rect];
}

And here is the result:

incorrect one

I've changed nothing but the draw method. How can I override the draw method while keeping my view obey the corner radius mask? Is this a bug in iOS or am I missing something?

like image 651
Can Poyrazoğlu Avatar asked Dec 06 '12 15:12

Can Poyrazoğlu


2 Answers

I don't know the full answer, but I do know that UIView's implementation of drawLayer:inContext: works differently depending on whether you have implemented drawRect: or not. Perhaps masking/clipping to bounds is one of those things it does differently.

You can try solving your issue a number of ways:

  • make your background transparent:

        layer.backgroundColor = [UIColor clearColor].CGColor;
    
  • clip yourself inside your custom drawRect::

    - (void)drawRect:(CGRect)rect {
        [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:30.0] addClip];
        [image drawInRect:rect];  // or whatever
    }
    
  • carve out the corners explicitly:

    CGContextBeginPath(c);
    CGContextAddArc(c, r, r, r, M_PI, 3*M_PI_2, 0);
    CGContextAddLineToPoint(c, 0, 0);
    CGContextClosePath(c);
    CGContextClip(c);
    [[UIColor grayColor] setFill];
    UIRectFill(rect);
    

I stole those last 2 suggestions from this great presentation from WWDC 2010: Advanced Performance Optimization on iPhone OS (video listed at this index page -- annoyingly, no direct link).

like image 195
AlexChaffee Avatar answered Nov 03 '22 00:11

AlexChaffee


This is an old question, but I was recently reading a useful blog post titled Abusing UIView. In it the author advises against overriding drawRect to do things like add a border when there are other ways of doing it. He says,

Overriding drawRect: causes a performance hit. I haven’t profiled it, so I don’t know if it’s a signifigant performance under normal circumstances (it’s probably not), but in most cases, folks who override drawRect: can accomplish what they want much easier by setting properties on a view’s layer property. If you need to draw an outline around a view, here’s how you would do it:

#import <QuartzCore/QuartzCore.h>

- (id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  if (self) {
      self.layer.borderWidth = 2.f;
      self.layer.borderColor = [UIColor redColor].CGColor;
  }
  return self;

I realize this probably doesn't solve the problem of the black background showing through. It is just something else to think about.

like image 42
Suragch Avatar answered Nov 02 '22 23:11

Suragch