【问题标题】:UIViewController returns invalid frame?UIViewController 返回无效帧?
【发布时间】:2012-03-21 07:59:15
【问题描述】:

当我在landscape 模式下启动 ViewController 时(调用 viewDidLoad 之后),我打印帧它给我纵向模式的帧大小。

这是一个错误有什么建议吗?

- (void)viewDidLoad
{
   [super viewDidLoad];

   NSLog(@"%@", NSStringFromCGRect(self.view.frame));
   // Result is (0, 0 , 768, 1024)
}

【问题讨论】:

  • 那么 viewDidAppear 呢?

标签: ios uiviewcontroller landscape


【解决方案1】:

这是一个替代解决方案(Swift 4):

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.setNeedsLayout()
    self.view.layoutIfNeeded()
}

基本上,这会强制视图控制器正确布局它的视图,并且从那里您可以获得所有子视图的正确帧。这在制作过渡动画并且您的视图控制器正在使用自动布局和界面构建器时特别有用。

据我所知,初始帧似乎设置为您的界面构建器的默认大小类设置为。我通常使用 iPhone XS 尺寸类进行编辑,因此在viewDidLoad 中,无论您是否使用 iPhone XR,视图的宽度似乎始终为 375。不过,这会在 viewWillAppear 之前自行纠正。

上面的代码将纠正这个问题,并允许您在视图控制器呈现到屏幕之前为您的视图/子视图获取正确的帧。

【讨论】:

    【解决方案2】:

    这是一个干净的解决方案。

    我遇到了同样的问题,我坚持在我的应用程序中使用frame。我不想让根视图控制器例外。我注意到根视图控制器的 frame 显示纵向尺寸,但所有子视图都有正确的横向尺寸

    由于只有根视图控制器的行为不同,我们可以将标准的空白视图控制器设置为根视图控制器,并将我们的自定义视图控制器添加到其中。 (代码如下。)

    然后,您可以在自定义视图控制器中按您的意愿使用frame

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
    
        self.window.rootViewController = [[UIViewController alloc] init];
    
        [self.window makeKeyAndVisible];
    
        self.viewController = [[ViewController alloc] initWithNibName:nil bundle:nil];
        [self.window.rootViewController addChildViewController:self.viewController];
        [self.window.rootViewController.view addSubview:self.viewController.view];
    
        return YES;
    }
    

    【讨论】:

      【解决方案3】:

      有几件事你不明白。

      首先,系统会在加载您的笔尖后立即向您发送viewDidLoad。它甚至还没有将视图添加到视图层次结构中。所以它也没有根据设备的旋转调整你的视图大小。

      其次,视图的框架位于其父视图的坐标空间中。如果这是您的根视图,它的超级视图将是UIWindow(一旦系统实际将您的视图添加到视图层次结构中)。 UIWindow 通过设置其子视图的变换来处理旋转。这意味着视图的框架不一定是您所期望的。

      这是纵向视图层次结构:

      (lldb) po [[UIApp keyWindow] recursiveDescription]
      (id) $1 = 0x09532dc0 <UIWindow: 0x9632900; frame = (0 0; 768 1024); layer = <UIWindowLayer: 0x96329f0>>
         | <UIView: 0x9634ee0; frame = (0 20; 768 1004); autoresize = W+H; layer = <CALayer: 0x9633b50>>
      

      这是横向左方向的视图层次结构:

      (lldb) po [[UIApp keyWindow] recursiveDescription]
      (id) $2 = 0x09635e70 <UIWindow: 0x9632900; frame = (0 0; 768 1024); layer = <UIWindowLayer: 0x96329f0>>
         | <UIView: 0x9634ee0; frame = (20 0; 748 1024); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x9633b50>>
      

      请注意,在横向上,帧大小是 748 x 1024,不是 1024 x 748。

      如果这是您的根视图,您可能想要查看的是视图的边界:

      (lldb) p (CGRect)[0x9634ee0 bounds]
      (CGRect) $3 = {
        (CGPoint) origin = {
          (CGFloat) x = 0
          (CGFloat) y = 0
        }
        (CGSize) size = {
          (CGFloat) width = 1024
          (CGFloat) height = 748
        }
      }
      

      大概您想知道视图的变换、框架和边界何时更新。如果您的视图控制器加载其视图时界面处于横向,您将按以下顺序接收消息:

      {{0, 0}, {768, 1004}} viewDidLoad
      {{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
      {{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
      {{0, 0}, {768, 1004}} viewWillAppear:
      {{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
      {{0, 0}, {768, 1004}} shouldAutorotateToInterfaceOrientation:
      {{0, 0}, {768, 1004}} willRotateToInterfaceOrientation:duration:
      {{0, 0}, {1024, 748}} viewWillLayoutSubviews
      {{0, 0}, {1024, 748}} layoutSubviews
      {{0, 0}, {1024, 748}} viewDidLayoutSubviews
      {{0, 0}, {1024, 748}} willAnimateRotationToInterfaceOrientation:duration:
      {{0, 0}, {1024, 748}} shouldAutorotateToInterfaceOrientation:
      {{0, 0}, {1024, 748}} viewDidAppear:
      

      您可以看到视图的边界在收到willRotateToInterfaceOrientation:duration: 之后 和在收到viewWillLayoutSubviews 之前之前 发生变化。

      viewWillLayoutSubviewsviewDidLayoutSubviews 方法是 iOS 5.0 的新方法。

      layoutSubviews 消息被发送到视图,而不是视图控制器,所以如果你想使用它,你需要创建一个自定义的 UIView 子类。

      【讨论】:

      • 我听过的最好的解释之一。谢谢
      • 对我来说关键是使用边界而不是框架。谢谢!
      • 很高兴知道,真的。
      • 我在这个主题上见过的最好的。谢谢。
      • 我在每个方法中都放了一个NSLog
      猜你喜欢
      • 2013-12-01
      • 1970-01-01
      • 2011-12-21
      • 2019-07-08
      • 1970-01-01
      • 2019-08-07
      • 2011-10-26
      • 2012-02-07
      相关资源
      最近更新 更多