UIViewController возвращает неверный кадр? - PullRequest
38 голосов
/ 03 марта 2012

Когда я запускаю свой ViewController в режиме landscape (после вызова viewDidLoad), я печатаю кадр, вместо этого он дает мне размер кадра для портретного режима.

Это ошибка, какие-либо предложения?

- (void)viewDidLoad
{
   [super viewDidLoad];

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

Ответы [ 3 ]

158 голосов
/ 03 марта 2012

Есть пара вещей, которые вы не понимаете.

Во-первых, система отправляет вам 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.

Методы viewWillLayoutSubviews и viewDidLayoutSubviews являются новыми для iOS 5.0.

Сообщение layoutSubviewsотправляется в представление, а не в контроллер представления, поэтому вам нужно будет создать собственный подкласс UIView, если вы хотите его использовать.

3 голосов
/ 19 июня 2013

Вот чистое решение.

У меня возникла та же проблема, и я настаиваю на использовании 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;
}
0 голосов
/ 09 мая 2019

Вот альтернативное решение ( Swift 4 ):

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

В основном это вынуждает контроллер представления корректно размещать свой вид, и оттуда вы можете получить правильные кадры для всехваши подпредставления.Это особенно помогает, когда выполняется анимация перехода, а ваши контроллеры представления используют autolayout и построитель интерфейса.

Из того, что я заметил, похоже, что начальные кадры установлены в соответствии с тем, какой класс размера вашего строителя интерфейса установлен по умолчанию.Я обычно редактирую, используя класс размера iPhone XS, поэтому в viewDidLoad кажется, что ширина представления всегда равна 375 независимо от того, используете ли вы iPhone XR или нет.Это исправляет себя до viewWillAppear, хотя.

Приведенный выше код исправит эту проблему и позволит вам получить правильные кадры для вашего представления / подпредставления до того, как контроллер представления будет отображен на экране.

...