Unable to set frame correctly before viewDidAppear

nacross picture nacross · Oct 26, 2012 · Viewed 27.7k times · Source

I was wondering if anyone knows why when you set the frame of a subview in viewDidLoad and viewWillAppear the changes do not take affect on the screen, but if you set it in viewDidAppear they do?

In my case I am loading a custom xib with two tableviews then attempting to shift them down in viewDidLoad to allow space for another view which is added in viewDidLoad as it is not always necessary to display it.

The problem is when i set frame in viewDidLoad or viewWillAppear it is set on the object, i can see by printing it out, but it is not reflected on screen. Moving my set frame calls to viewDidAppear will cause everything to work as expected.

Is it wrong to think I should be able to set the frame in viewDidLoad?

- (id)init {
    if ( self = [super initWithNibName:@"MyView" bundle:nil] ) {}

    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.descriptionWebView = [[[UIWebView alloc] initWithFrame:CGRectMake( 0, 0, self.view.frame.size.width, 200 )] autorelease];

    [self.view addSubview:self.descriptionWebView];

    self.tableView1.autoresizingMask = UIViewAutoresizingNone;
    self.tableView2.autoresizingMask = UIViewAutoresizingNone;

    [self.descriptionWebView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"...." withExtension:@"html"]]];

    table1LocationWithHeader = CGRectMake( self.tableView1.frame.origin.x, 200, self.tableView1.frame.size.width, self.tableView1.frame.size.height - 200 );
    table2LocationWithHeader = CGRectMake( self.tableView2.frame.origin.x, 200, self.tableView2.frame.size.width, self.tableView2.frame.size.height - 200 );

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}


- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    //this WILL work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}


//I added these after comments for stack overflow users, it seems like viewDidLayoutSubviews is the best place to set the frame

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    //this will NOT work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    //this WILL work
    self.tableView1.frame = table1LocationWithHeader;
    self.tableView2.frame = table2LocationWithHeader;
}

Answer

Leon Storey picture Leon Storey · Oct 26, 2012

viewDidLoad is called when the class is loaded however no ui elements have been initialised and therefore any attempt to reference them will be overwritten or unavaliable during the initialisation process which happens between the viewDidLoad and viewDidAppear calls. Once all ui element have been initalised and drawn viewDidAppear is called.

viewDidLoad - Called after the controller's view is loaded into memory

At this point the view isn't within the view hierarchy.

viewWillAppear - Notifies the view controller that its view is about to be added to a view hierarchy.

Again, the view is yet to be added to the view hierarchy.

viewDidAppear - Notifies the view controller that its view was added to a view hierarchy.

Only then is the view added to the view hierarchy.

Update

The viewDidLayoutSubviews is the most appropriate place to modify the UI before it actually appears on the screen.

viewDidLayoutSubviews - Notifies the view controller that its view just laid out its subviews.