iosobjective-cuiviewuiimageviewinitwithframe

initWithImage works fine initWithFrame is being called more than once


I am sublassing either an UIImageView or UIView for generating simple color tiles with digits. If I am using UIImageView, I use initWithImage method. If I use UIView, the method initWithFrame is being used. Either red square image or programmatically generated red view is used for initialization.
The problem can be seen on two screenshots: when initWithImage is being used - everything works fine. If initWithFrame method is being used, I am ending with multiple white views without any information created in even order with normal views. All the screenshots and code attached.

This how it looks when initializing using initWithImage: initWithImage

- (id)initWithImage:(UIImage *)image {
    self = [super initWithImage:image];
    if (self) {
//Some non-important, label-related stuff.
        [self addSubview:self.numberLabel];
    }
    return self;
}

And this is how it looks with initWithFrame:
initWithFrame

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.redView = [self redViewWithFrame:frame];
        [self addSubview:self.redView];
//label-related stuff
    }
    return self;
}


- (UIView *)redViewWithFrame:(CGRect)frame {
    UIView *view = [[UIView alloc] initWithFrame:frame];
    view.backgroundColor = [UIColor redColor];
    view.alpha = 1;
    return view;
}    

And the for-loop, calling these initializers from another class (UIScrollView subclass). The label value is being set after the view had been initialized.

- (void)setNumberOfBlocks:(NSInteger)numberOfBlocks {
    _blocks = [[NSMutableArray alloc] init];

    CGSize contentSize;
    contentSize.width = BLOCK_WIDTH * (numberOfBlocks);
    contentSize.height = BLOCK_HEIGHT;

    self.contentSize = contentSize;

    for (int i = 0; i < numberOfBlocks; i++) {
        CGFloat totalWidth = BLOCK_WIDTH * i;
        CGRect frame = CGRectMake(0, 0, BLOCK_WIDTH, BLOCK_HEIGHT);

        frame.origin.x = totalWidth;


        BlockView *view = [[BlockView alloc] initWithImage:[UIImage imageNamed:@"block.png"]];
        OR!!!
        BlockView *view = [[BlockView alloc] initWithFrame:frame];


        view.frame = frame;
        NSString *number = [NSString stringWithFormat:@"%ld", (long)i + 1];
        view.numberLabel.text = number;


        [self addSubview:view];
        [_blocks addObject:view];
    }
}

Googling gave me the impression that this is very common problem, but I haven't found any solution how to beat this. Moreover, I still do not understand, why numbers are in totally right order, the only problem is view position.


Solution

  • The problem is that you're setting the frame of the red view to the superview's frame instead of its bounds. Since the red view is a subview of the BlockView, its frame needs to be relative to its superview, which you get with bounds (its not clear why you even need the red view, as opposed to setting the background color of the block view to red).

    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.redView = [self redViewWithFrame:self.bounds];
            [self addSubview:self.redView];
    //label-related stuff
        }
        return self;
    }