Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
355 views
in Technique[技术] by (71.8m points)

objective c - 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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...