6

I have a storyboard which loads loads a custom UIView. Also a sub view is added to the view in the storyboard. It worked fine until I overwrote the drawRect method of the sub view, then I just saw a black rectangle instead of the subview. Here is the code:

#import <UIKit/UIKit.h>
#import "MySubview.h"

@interface MyView : UIView

@end

#import "MyView.h"

@implementation MyView

- (void) awakeFromNib
{
    CGRect frame = [self frame];
    MySubview* sv = [[MySubview alloc] initWithFrame:frame];
    [self addSubview:sv];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

@end

#import <UIKit/UIKit.h>

@interface MySubview : UIView

@property (retain, nonatomic) NSString* text;
@property (retain, nonatomic) UILabel* label;

@end

#import "MySubview.h"

@implementation MySubview

@synthesize text, label;


- (void)attachLabel
{
    text = @"Hello";
    label = [[UILabel alloc] init];
    [label setText:text];
    [label setFont:[UIFont fontWithName:@"Futura" size:18]];
    [label setBackgroundColor:[UIColor clearColor]];

    [label sizeToFit];

    CGRect labelFrame = label.frame;
    labelFrame.origin.x = (self.frame.size.width  - labelFrame.size.width) / 2;
    labelFrame.origin.y = (self.frame.size.height - labelFrame.size.height) / 2;
    label.frame = labelFrame;

    [self addSubview:label];
}

- (void)awakeFromNib
{
    [self attachLabel];
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self attachLabel];
    }
    return self;
}

// Works if I comment this out!
- (void)drawRect:(CGRect)rect
{
}

@end

Update - Added drawing code below:

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

    // Parameters used for drawing.
    const CGFloat lineWidth = 5;
    const CGFloat shadowOffset = 3;
    const CGFloat shadowBlur = 4;
    const CGFloat spaceToBB = 10;   // Space to the bounding box of this view.
    const CGFloat cornerRadii = 5;
    const CGFloat lineColor[4] = { 0, 0, 0, 1 };

    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextSetLineWidth(ctx, lineWidth);
    CGContextSetStrokeColor(ctx, lineColor);
    CGContextSetShadow(ctx, CGSizeMake(shadowOffset, shadowOffset), shadowBlur);

    CGRect innerRect = rect;

    innerRect.size.width -= 2*spaceToBB;
    innerRect.size.height -= 2*spaceToBB;
    innerRect.origin.x += spaceToBB;
    innerRect.origin.y += spaceToBB;

    UIBezierPath *path = 
    [UIBezierPath bezierPathWithRoundedRect:innerRect 
                          byRoundingCorners:UIRectCornerAllCorners 
                                cornerRadii:CGSizeMake(cornerRadii, cornerRadii)
     ];

    CGContextAddPath(ctx, path.CGPath);
    CGContextStrokePath(ctx);
}


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

Update

It seems to work when I fill the bounding box of the sub view with some color first.

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat white[] = {1, 1, 1, 0.5};
CGContextSetFillColor(ctx, white);
CGContextAddRect(ctx, rect);
CGContextFillPath(ctx);
Nils
  • 13,319
  • 19
  • 86
  • 108
  • When does this `- (void)drawRectWithRoundBorders:(CGRect)rect ` get called? – jrturton Jan 03 '12 at 15:43
  • @jrturton in drawRect, just scroll down a bit – Nils Jan 03 '12 at 17:52
  • Oh right, I was confused by the empty implementation above - you should probably remove that from the question (and your code??) – jrturton Jan 03 '12 at 18:48
  • Note also that the `rect` in drawRect is not necessarily the entire bounds of your view - you appear to use it as such in your method, but you should really be using self.bounds. – jrturton Jan 03 '12 at 19:54
  • 1
    The opaque property was what I was looking for! Setting it to NO resulted in the desired behavior. – Nils Jan 13 '12 at 11:35
  • Please select the correct answer below, since you also discovered that it works. – mahboudz Jan 11 '15 at 23:59

2 Answers2

41

Just add [self setOpaque:NO] in your initWithFrame: method.

spassas
  • 4,778
  • 2
  • 31
  • 39
Marc Matta
  • 616
  • 7
  • 13
4

That's because your drawRect is doing nothing. You override drawRect if you want to do custom drawing. So:

  • If you don't want to do custom drawing then don't override drawRect.
  • If you do want to do custom drawing then actually do something in drawRect.
mattjgalloway
  • 34,792
  • 12
  • 100
  • 110
  • Hummm but if I load the subview class directly in a storyboard it works. Also If I draw something in drawRect it is still black. – Nils Jan 03 '12 at 11:29
  • or add `[super drawRect:rect]` if you want to draw ontop of the default view :) – deanWombourne Jan 03 '12 at 11:29
  • What's your drawing code to draw something? If it's still black then you're drawing it wrongly. – mattjgalloway Jan 03 '12 at 11:34
  • Try just drawing a single line to start with - then work up to all that you're trying to achieve. And your background colour is transparent - was that deliberate? – deanWombourne Jan 03 '12 at 11:48
  • Well i just added the background thing later to see weather it changes anything. Point is that it is black if used as subview and it works when I use it directly in the storyboard and I would really like to know why this. – Nils Jan 03 '12 at 12:11
  • Have you tried calling `setNeedsDisplay` after adding it when using it directly as a subview? – mattjgalloway Jan 03 '12 at 12:15
  • Yes, it had no effect. I assume that setNeedsDisplays propagates throughout the subview tree once it is called on the top view. – Nils Jan 03 '12 at 12:35
  • Why did you assume that calling setNeedsDisplay should change anything? – Nils Jan 03 '12 at 12:39
  • 5
    What is the `opaque` property of the view set as? In a Storyboard/xib file the property may default to `NO`, but instantiating the view another way may leave the property as `YES`. – sho Jan 03 '12 at 17:32
  • I just thought it's worth checking. – mattjgalloway Jan 03 '12 at 17:36