当我在landscape
模式下启动我的ViewController时(在viewDidLoad被调用之后),它给我的frame大小是竖屏模式的大小。
这是一个bug吗?有什么建议吗?
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@", NSStringFromCGRect(self.view.frame));
// Result is (0, 0 , 768, 1024)
}
当我在landscape
模式下启动我的ViewController时(在viewDidLoad被调用之后),它给我的frame大小是竖屏模式的大小。
这是一个bug吗?有什么建议吗?
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@", NSStringFromCGRect(self.view.frame));
// Result is (0, 0 , 768, 1024)
}
有几件事情你没有理解。
首先,系统在加载你的nib后立即发送给你 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。
如果这是您的根视图,则您可能想查看视图的边界(bounds):
(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
方法之间,你的视图边界会发生变化。
viewWillLayoutSubviews
和 viewDidLayoutSubviews
方法是 iOS 5.0 新增的。
layoutSubviews
消息被发送给视图而不是视图控制器,所以如果你想使用它,你需要创建一个自定义的 UIView
子类。NSLog
。 - rob mayoffframe
。我不想为根视图控制器做出例外。我注意到根视图控制器的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;
}
这里是另一种解决方案(Swift 4):
override func viewDidLoad() {
super.viewDidLoad()
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
基本上,这会强制视图控制器正确布局其视图,从而您可以获得所有子视图的正确框架。当执行转换动画并且您的视图控制器正在使用自动布局和界面构建器时,这特别有帮助。
从我注意到的情况来看,初始框架似乎设置为您的界面构建器的默认大小类别。我通常使用iPhone XS大小类别进行编辑,因此在viewDidLoad
中,无论您是否使用iPhone XR,视图的宽度似乎始终为375。不过,在viewWillAppear
之前,这个问题就会得到纠正。
上述代码将纠正此问题,并允许您在视图控制器呈现到屏幕之前获取视图/子视图的正确框架。