Подтвердить что ты не робот

UIViewController возвращает недопустимый фрейм?

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

Есть ли у вас какие-либо предложения?

- (void)viewDidLoad
{
   [super viewDidLoad];

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

Ответ 1

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

Сначала система отправляет вам 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, если вы хотите его использовать.

Ответ 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

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

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

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

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

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