views:

114

answers:

2

According to Apple's docs, "Subclasses need not override -[UIView drawRect:] if the subclass is a container for other views."

I have a custom UIView subclass that is indeed merely a container for other views. Yet the contained views aren't getting drawn. Here's the pertinent code that sets up the custom UIView subclass:

- (id)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame]))
    {
        // Consists of both an "on" light and an "off" light. We flick between the two depending upon our state.
        self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
        self.onLight.backgroundColor = [UIColor clearColor];
        self.onLight.on = YES;
        [self addSubview:self.onLight];

        self.offLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
        self.offLight.backgroundColor = [UIColor clearColor];
        self.offLight.on = NO;
        [self addSubview:self.offLight];

        self.on = NO;
    }
    return self;
}

When I run the code that displays this custom UIView, nothing shows up. But when I add a drawRect method...

    - (void)drawRect:(CGRect)rect
    {
        [self.onLight drawRect:rect];
        [self.offLight drawRect:rect];
    }

...the subviews display. (Clearly, this isn't the right way to be doing this, not only because it's contrary to what the docs say, but because it -always- displays both subviews, completely ignoring some other code in my UIView that sets the hidden property of one of the views, it ignores the z-ordering, etc.)

Anyway, the main question: why don't my subviews display when I'm not overriding drawRect:?

Thanks!

UPDATE:

Just to make sure that the problem doesn't lie in my custom subviews, I added in a UILabel as well. So the code reads:

- (id)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame]))
    {
        // Consists of both an "on" light and an "off" light. We flick between the two depending upon our state.
        self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
        self.onLight.backgroundColor = [UIColor clearColor];
        self.onLight.on = YES;
        [self addSubview:self.onLight];

        self.offLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
        self.offLight.backgroundColor = [UIColor clearColor];
        self.offLight.on = NO;
        [self addSubview:self.offLight];

        self.on = NO;

        UILabel* xLabel = [[[UILabel alloc] initWithFrame:frame] autorelease];
        xLabel.text = @"X";
        [self addSubview:xLabel];
    }
    return self;

The "X" doesn't display either.

UPDATE 2:

Here's the code that invokes my custom UIView (OffOnLightView):

            // Container for all of the OffOnLightViews...
            self.stampSuperView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease];
            [self.view addSubview:self.stampSuperView];

            // Draw the stamps into the 'stamp superview'.
            NSInteger numberOfCardSpaces = (awardType == None) ? 3 : 10;
            for (NSInteger i = 1; i <= numberOfCardSpaces; i++)
            {
                OffOnLightView* newNumberView = [[[OffOnLightView alloc] initWithFrame:[self frameForStampWithOrdinal:i awardType:awardType]] autorelease];
                newNumberView.on = (i <= self.place.checkInCount.intValue);
                newNumberView.number = [NSString stringWithFormat:@"%d", i];
                [self.stampSuperView addSubview:newNumberView];
            }
+1  A: 

You should never call -drawRect: manually. If you need to force a redraw, call -setNeedsDisplay.

I would start by debugging (breakpoint or NSLog()) in the -drawRect: methods of the two subviews you are adding to make sure they are actually performing their drawing.

Also note how you're making both subviews the full size (frame) of the containing view, and setting their background colours to clear. I'm going to guess this is intentional, but it's possible they are displaying, but you just can't see anything due to them having a transparent background.

jbrennan
See update above...
Greg Maletic
+1  A: 

Your subviews should have their frame initialized to the bounds of the parent uiview. Subviews are in a different coordinate system that is relative to the frame of the parent.

self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:self.bounds] autorelease];
progrmr
Ka-ching! That's it. Stupid mistake; thanks for the advice!
Greg Maletic
I've made the same mistake a couple times with Popovers.
progrmr