Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS bounds change after rotating to landscape, then back to portrait

When my application launches, an NSLog of my view's bounds shows the following:

NSLog(@"My view frame: %@", NSStringFromCGRect(self.view.bounds));
My view frame: {{0, 0}, {320, 548}}

I then rotate the simulator and get the following:

NSLog(@"My view frame: %@", NSStringFromCGRect(self.view.bounds));
My view frame: {{0, 0}, {320, 548}}

Then when I rotate the simulator back to portait, I get this:

NSLog(@"My view frame: %@", NSStringFromCGRect(self.view.bounds));
My view frame: {{0, 0}, {568, 300}}

The two methods I'm using to adjust elements are these, they aren't done yet, as I'm currently in the middle of fixing them for the new iphone.

-(void) resizeForLandscape
{
    newsLabel = (UILabel *)[self.view viewWithTag:50];
    weatherLabel = (UILabel *)[self.view viewWithTag:51];
    sportsLabel = (UILabel *)[self.view viewWithTag:52];
    entertainmentLabel = (UILabel *)[self.view viewWithTag:53];
    videosLabel = (UILabel *)[self.view viewWithTag:54];
    moreLabel = (UILabel *)[self.view viewWithTag:55];

    NSLog(@"resizeForLandscape called");
    webView.frame = CGRectMake(0, 31, self.view.frame.size.width, self.view.frame.size.height - 75);    
    button.frame = CGRectMake(0, 0, image.size.width -30, image.size.height -15);
    myLabel.frame = CGRectMake(230, 100, 80, 21);
    label.frame = CGRectMake(0, 0, self.view.bounds.size.height + 15, 30);

    NSLog(@"My view frame: %@", NSStringFromCGRect(self.view.bounds));
    NSLog(@"my status bar height is %f", [UIApplication sharedApplication].statusBarFrame.size.height);
    NSLog(@"my status bar width is %f", [UIApplication sharedApplication].statusBarFrame.size.width);


    newsLabel.frame = CGRectMake((self.view.bounds.size.height - 346) / 2 + 12, self.view.bounds.size.width - 38, 43, 21);
    weatherLabel.frame = CGRectMake(newsLabel.frame.origin.x + 64, self.view.bounds.size.width - 38, 42, 21);
    sportsLabel.frame = CGRectMake(newsLabel.frame.origin.x + 126, self.view.bounds.size.width - 38, 42, 21);
    entertainmentLabel.frame = CGRectMake(newsLabel.frame.origin.x + 185, self.view.bounds.size.width - 38, 66, 21);
    videosLabel.frame = CGRectMake(newsLabel.frame.origin.x + 269, self.view.bounds.size.width - 38, 42, 21);
    moreLabel.frame = CGRectMake(newsLabel.frame.origin.x + 325, self.view.bounds.size.width - 38, 42, 21);

    spacer1.width = (self.view.bounds.size.height - 346) / 2;
    spacer2.width = 40;
    spacer3.width = 28;
    spacer4.width = 42;
    spacer5.width = 35;
    spacer6.width = 24;
}

-(void) resizeForPortrait
{
    newsLabel = (UILabel *)[self.view viewWithTag:50];
    weatherLabel = (UILabel *)[self.view viewWithTag:51];
    sportsLabel = (UILabel *)[self.view viewWithTag:52];
    entertainmentLabel = (UILabel *)[self.view viewWithTag:53];
    videosLabel = (UILabel *)[self.view viewWithTag:54];
    moreLabel = (UILabel *)[self.view viewWithTag:55];

    NSLog(@"resizeForPortrait called");

    webView.frame = CGRectMake(0, 44, self.view.frame.size.width, self.view.frame.size.height -88);
    button.frame = CGRectMake(0, 0, image.size.width, image.size.height);
    myLabel.frame = CGRectMake(145, 152, 80, 21);
    label.frame = CGRectMake(0, 0, 315, 40);

    NSLog(@"My view frame: %@", NSStringFromCGRect(self.view.bounds));

    NSLog(@"my status bar height is %f", [UIApplication sharedApplication].statusBarFrame.size.height);
    NSLog(@"my status bar width is %f", [UIApplication sharedApplication].statusBarFrame.size.width);


    float myWidth = MIN(self.view.bounds.size.height, self.view.bounds.size.width);
    float myHeight = MAX(self.view.bounds.size.height, self.view.bounds.size.width);

    newsLabel.frame = CGRectMake(newsLabel.frame.origin.x, myHeight - 18, 43, 21);
    weatherLabel.frame = CGRectMake(newsLabel.frame.origin.x + 43, myHeight - 18, 42, 21);
    sportsLabel.frame = CGRectMake(newsLabel.frame.origin.x + 97, myHeight - 18, 42, 21);
    entertainmentLabel.frame = CGRectMake(newsLabel.frame.origin.x + 138, myHeight - 18, 66, 21);
    videosLabel.frame = CGRectMake(newsLabel.frame.origin.x + 213, myHeight - 18, 42, 21);
    moreLabel.frame = CGRectMake(newsLabel.frame.origin.x + 258, myHeight - 18, 42, 21);


    spacer1.width = (myWidth - 346) / 2;
    spacer2.width = 20;
    spacer3.width = 18;
    spacer4.width = 25;
    spacer5.width = 28;
    spacer6.width = 14;
}

Called by:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        [self resizeForLandscape];
    } else {
        [self resizeForPortrait];
    }
}

- (void) getOrientationandResize
{    
    if (UIDeviceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        [self resizeForLandscape];
    } else {
        [self resizeForPortrait];
    }
}

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

My head is about to explode. Not only are the views reversed, but 20 pixels have been robbed from the width and given to the height. Can anyone explain this to me and how I could/should code the elements in my view to account for it?

My storyboard is as follows:

enter image description here

My initial view: enter image description here

After first rotation (still good) enter image description here

Rotating back to portrait (all screwed up because of the bounds values) enter image description here

like image 862
Sonny Parlin Avatar asked Sep 13 '12 18:09

Sonny Parlin


1 Answers

You should pretty much never do layout in willRotateToInterfaceOrientation:duration:. When the system sends you willRotateToInterfaceOrientation:duration:, it has not updated your view's frame to its new size. So by consulting self.view.frame, you're using the frame for the current (old) orientation, not the frame for the new (not-yet-rotated-to) orientation.

You should also not do layout in viewDidAppear:, because by the time you receive viewDidAppear:, your view is already on the screen. You need to do layout before your view is on the screen.

You need to do your layout in an appropriate method. The best place to do layout is in the layoutSubviews method of a custom UIView subclass. Putting it there keeps your view logic separate from your controller logic.

The next-best place to do your layout is in the viewWillLayoutSubviews or viewDidLayoutSubviews method of your view controller.

If you put your layout code in layoutSubviews, viewWillLayoutSubviews, or viewDidLayoutSubviews, the system will automatically call that method before your view appears on-screen, and inside the animation block of an autorotation. In both cases, it will update your view's frame for the correct orientation before sending you the message.

If you need to do something like hide a view before the autorotation animation starts, and show it again after, you can do that in willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation:.

If you want to perform special animations only during an autorotation, and not during initial view layout, you can do that in willAnimateRotationToInterfaceOrientation:duration:, which is called inside the animation block of an autorotation.

You may find this answer helpful: UIViewController returns invalid frame?

like image 141
rob mayoff Avatar answered Sep 21 '22 19:09

rob mayoff