iPhone Пейзаж FAQ и решения - PullRequest
18 голосов
/ 02 июня 2010

Здесь было много путаницы и множество соответствующих вопросов о том, как можно реализовать приложения iPhone с надлежащей обработкой для автоповорота в альбомном / портретном режиме. Особенно сложно реализовать такое приложение, когда требуется запуск в ландшафтном режиме. Наиболее распространенным наблюдаемым эффектом являются зашифрованные макеты и области экрана, где касания больше не распознаются.

Простой поиск вопросов, помеченных iphone и landscape, выявляет следующие проблемы, возникающие при определенных сценариях:

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

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

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

Ответы [ 6 ]

13 голосов
/ 02 июня 2010

Что в документации :

В вашем контроллере представления переопределите shouldAutorotateToInterfaceOrientation: объявить поддерживаемые ориентации интерфейса. Это свойство будет / должно проверяться инфраструктурой контроллера при каждом изменении ориентации устройства.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
   return  (orientation == UIInterfaceOrientationLandscapeRight);
}

Это абсолютный минимум, который необходим вашему контроллеру представления. Если вы хотите запустить приложение в альбомном режиме, вам необходимо добавить следующий ключ в ваш файл .plist:

<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>

Apple рекомендует запускать приложения только в альбомной ориентации в режиме альбомной ориентации (см. HIG в разделе Руководства по работе с пользователем> Начать мгновенно).

Чего нет в документации:

Небольшой фон:

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

Другие люди предложили использовать «MasterViewController», подключенный к главному окну, к которому другие контроллеры добавляют свои представления в качестве подпредставлений, а не подключать непосредственно к окну. Хотя я нашел, что это решение является жизнеспособным вариантом, оно не работает должным образом в случае модальных контроллеров представления, добавленных к тем упомянутым подпредставлениям. Также существует проблема, если у вас есть некоторые подпредставления, которые должны иметь возможность автоматического поворота (что будет предотвращать главный контроллер).

Использование недокументированных API для навязывания определенной ориентации интерфейса также не вариант.

Решение:

Лучшее решение, которое я нашел на данный момент, - это модификация обходного пути MasterViewController. Вместо использования собственного «MasterViewController» используется UINavigationController со скрытой панелью навигации и скрытой панелью вкладок. Если все другие представления будут вытолкнуты / извлечены из стека навигации этого контроллера, автоматическое вращение контроллеров в этом стеке будет управляться правильно.

Модальные контроллеры, представленные через presentModalViewController:animated: от любого из контроллеров представления в стеке навигации UINavigationController, будут вращаться и отображаться с правильной компоновкой. Если вы хотите, чтобы ваш модальный контроллер вида мог вращаться с ориентацией, отличной от ориентации родительского контроллера вида, вам необходимо вернуть желаемую ориентацию из метода shouldAutorotateToInterfaceOrientation родительского контроллера , пока модальный вид представил. Чтобы правильно восстановить ориентацию интерфейса, когда модальный контроллер отключен, вам необходимо убедиться, что shouldAutorotateToInterfaceOrientation возвращает желаемую ориентацию для родительского контроллера, прежде чем вы вызовете dismissModalViewController:animated:. Вы можете использовать личный BOOL на вашем контроллере представления для управления этим (например, BOOL isModalMailControllerActive_).

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

7 голосов
/ 13 апреля 2012

У меня было интересное требование для приложения ios:

main viewController должен быть только альбомным, но все остальные (которые можно вытолкнуть из основного) могут быть альбомными и портретными.

Проблема возникает - когда я нажимаю на новый viewController, который затем поворачивается к портретному изображению - и всплывающее окно - основной вид больше не является пейзажным. Также - открытие приложения, оно не в альбомной ориентации.

Чтобы сохранить ландшафт основного view-контроллера, независимо от того, с какой ориентации он был вытолкнут / нажат, я сделал следующее: (в viewWillAppear:)

//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];

//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[[UIViewController alloc] init] autorelease];
[self presentModalViewController:mVC animated:NO];
[self dismissModalViewControllerAnimated:NO];

Надеюсь, это кому-нибудь поможет!

P.S. Проверено на SDK 3.2.5 IOS 5.0.1.

P.S. Спасибо за всю информацию в этом FAQ!

3 голосов
/ 29 октября 2011

Что касается второго пункта, если вы хотите использовать pushViewController для перехода из режима «Только портрет» в режим «Только пейзаж», я обнаружил один простой способ взломать следующий код в viewDidLoad вашего контроллера:

UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
[viewController release];
2 голосов
/ 12 декабря 2012

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

Во время обновления или изменения ориентации представление галереи вычисляет количество отображаемых элементов на основе ширины (или высоты) экрана и текущей ориентации из UIViewController.interfaceOrientation.

У меня была проблема с тем, чтобы заставить это работать правильно. Иногда он отображал только два элемента в альбомной ориентации после того, как я закрыл модальное диалоговое окно.

Сначала я использовал значения UIViewController.view.frame.size для вычисления количества элементов галереи. Когда модальное представление было отклонено, этот размер кадра был неправильным, например ширина и высота были изменены на противоположные, хотя ориентация не изменилась, пока отображался модальный диалог.

Я переключился на использование делегата приложения ([[UIApplication sharedApplication] делегат]] и использования window.frame.size, чтобы вычислить количество элементов галереи для отображения. Размер window.frame.size остается правильным при изменении ориентации модальные диалоги. Элементы галереи теперь отображаются корректно.

2 голосов
/ 16 марта 2011

Я хотел бы добавить к ответу Йоханнеса (используя UINavigationController в качестве MasterViewController).

Недостаток, который я обнаружил, заключается в том, что ViewControllers, которые недавно помещены в стек навигации главного виртуального канала, не приспосабливаются к любым предшествующим изменениям ориентации. Короче говоря, VC, уже находящиеся в стеке, вращаются, контроллеры модального представления, представленные из них, также поворачиваются, но недавно добавленные VC не вращаются.

Я пробовал много хитростей, чтобы исправить это, прежде чем нашел тот, который работает. Большинство работает только для pushViewController: с анимированным значением YES.

Чтобы полностью устранить проблему, я подклассифицировал UINagivationController и переопределил pushViewController:animated: следующим образом:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // Correctly autoOrient the given view controller
    [self presentModalViewController:viewController animated:NO];
    [self dismissModalViewControllerAnimated:NO];

    // Push it as normal (now in its correct orientation)
    [super pushViewController:viewController animated:animated];
}

Временно (и совершенно незаметно) представление контроллера представления позволяет ему получать обновление ориентации. Затем его можно поместить в стек навигации в правильной ориентации.

Пожалуйста, дайте мне знать, если это работает для вас. Все обновления и улучшения приветствуются!

Наконец, я очень рекомендую подход Йоханнеса к управлению ротацией.

РЕДАКТИРОВАТЬ: Обновление контроллеров представления выталкивания из стека

Кажется, что все селекторы, связанные с popViewController, сходят с ума, когда выполняются с анимацией: ДА. В частности, контроллер представления анимирован в неправильном направлении. Вы можете использовать animated: NO и ограничивать использование таких анимаций другими UINavigationControllers, расположенными глубже в вашей иерархии (то есть теми, которые вы помещаете в стек корневого контроллера навигации).

Любой вклад приветствуется.

0 голосов
/ 22 октября 2012

Это будет работать ...

UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    UIView *view = [window.subviews objectAtIndex:0];
    [view removeFromSuperview];
    [window addSubview:view];
...