iOS在从竖屏旋转到横屏,再旋转回竖屏后,边界发生了变化。

13

当我的应用程序启动时,我的视图边界的NSLog输出如下:

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

然后我旋转模拟器,得到以下结果:

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

然后当我将模拟器旋转回纵向时,我得到了这个:

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

我正在使用以下两种方法来调整元素,它们还没有完成,因为我目前正在修复它们以适应新的 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;
}

被调用者:

-(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];
}

我快要头炸了。不仅视图被翻转了,而且宽度被减少了20像素,高度增加了20像素。有人能解释一下这是怎么回事吗?我该如何编写我的视图元素来应对这种情况?

我的故事板如下:

输入图像描述

我的初始视图: 输入图像描述

第一次旋转后 (仍然正常) 输入图像描述

旋转回纵向 (由于边界值的问题而全部错乱) 输入图像描述


我猜你的边界是在窗口边界之前设置的。至于那20像素,那将是顶部的状态栏。 - mkral
当您从委托中旋转时,调用了哪些方法?您是否使用IB? - mkral
当我的应用程序启动时,状态栏的宽度为320,高度为20。当我旋转到横向模式时,它保持不变,当我旋转回纵向模式时,它的高度变为568.000000,宽度为20.000000。 - Sonny Parlin
Shizam,是的,我相信如此。也就是说,我只需调用willRotateToInterfaceOrientation方法,检查方向,然后调整一些元素。 - Sonny Parlin
resizeForLandscape 是如何被调用的?请展示调用它的方法。 - rob mayoff
显示剩余7条评论
1个回答

48
你几乎永远不应该在 willRotateToInterfaceOrientation:duration: 中布局。当系统发送 willRotateToInterfaceOrientation:duration: 时,它还没有更新视图的框架到新大小。因此,通过查看 self.view.frame,你正在使用当前(旧)方向的框架,而不是新方向(尚未旋转到)的框架。
你也不应该在 viewDidAppear: 中进行布局,因为当你收到 viewDidAppear: 时,你的视图已经在屏幕上了。你需要在你的视图出现在屏幕之前进行布局。
你需要在适当的方法中进行布局。最好的地方是在自定义 UIView 子类的 layoutSubviews 方法中进行布局。将其放在那里将使您的视图逻辑与控制器逻辑分开。
下一个最佳位置是在视图控制器的 viewWillLayoutSubviewsviewDidLayoutSubviews 方法中进行布局。
如果你将你的布局代码放在 layoutSubviewsviewWillLayoutSubviewsviewDidLayoutSubviews中,则在你的视图出现在屏幕上之前,系统将自动调用该方法,并且在自动旋转的动画块中。在两种情况下,它将在向你发送消息之前更新视图的框架以适应正确的方向。
如果你需要像在自动旋转动画开始之前隐藏一个视图,并在之后再显示它,你可以在 willRotateToInterfaceOrientation:duration:didRotateFromInterfaceOrientation: 中进行这样的操作。
如果你想要仅在自动旋转期间执行特殊的动画,而不是在初始视图布局期间执行动画,你可以在 willAnimateRotationToInterfaceOrientation:duration: 中做这件事,该方法会在自动旋转的动画块内调用。

您可能会发现这个答案有所帮助:UIViewController返回无效的框架?


谢谢,我会尝试并回复您的! - Sonny Parlin
1
是的,viewDidLayoutSubviews 是解决我的问题的答案。 - Sonny Parlin
优秀的信息指导正确的做事方式。 (+1) - Sebastian Dwornik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接