Как ограничить авторотацию одной ориентацией для некоторых видов, позволяя при этом все ориентации на других?
Этот вопрос касается вращения устройства iOS и нескольких контролируемых представлений в UINavigationController. Некоторые взгляды должны быть ограничены ориентацией на портрет, а некоторые должны автоматически открываться. Если вы попытаетесь создать простейшую настройку с тремя видами, вы заметите, что поведение авторотации имеет несколько очень неприятных причуд. Сценарий, однако, очень прост, поэтому я думаю, что я либо не выполняю реализацию авторотации правильно, либо забываю что-то.
У меня есть очень базовое демонстрационное приложение, которое показывает странность, и я сделал видео, показывающее его в действии.
Настройка очень простая: три контроллера вида, называемые FirstViewController
, SecondViewController
и ThirdViewController
, расширяют AbstractViewController
, который показывает метку с именем класса и возвращает YES для shouldAutorotateToInterfaceOrientation:
, когда устройство находится в портретной ориентации. SecondViewController переопределяет этот метод, чтобы разрешить все вращения. Все три конкретных класса добавляют несколько цветных квадратов, чтобы можно было перемещаться между представлениями, нажимая и выталкивая контроллеры в положение UINavigationController
. Я бы сказал, что это очень простой сценарий.
Если вы держите устройство в портретной или альбомной ориентации, это результат, который я бы не только хотел достичь, но и ожидал. На первом изображении вы видите, что все представления являются "вертикальными", а во втором вы видите, что только второй контроллер просмотра вращает ориентацию устройства. Чтобы быть ясным, должно быть возможно перемещаться со второго представления в ландшафтном режиме на третье, но поскольку эта третья только поддерживает портретную ориентацию, ее следует показывать только в портретной ориентации. Самый простой способ убедиться, что результаты в порядке, - это посмотреть на позицию несущей.
![Expected view orientation for the device in portrait mode]()
![Expected view orientation for the device in landscape mode]()
Но этот вопрос здесь, потому что фактический результат совершенно другой. В зависимости от того, с каким видом вы находитесь, когда вы вращаете устройство, и в зависимости от того, какой вид вы переходите к следующему, представления не будут вращаться (чтобы быть конкретным, метод didOrientFromInterfaceOrientation:
никогда не вызывается). Если вы находитесь в ландшафте на втором и переходите к третьему, он будет иметь ту же ориентацию, что и второй (= плохой). Однако, если вы переходите со второго назад на первое, экран будет вращаться в режиме принудительного портрета, а панель оператора будет находиться на физическом верхнем углу устройства, независимо от того, как вы держите его. Видео показывает это более подробно.
![Actual view orientation for the device in landscape mode]()
Мой вопрос двоякий:
- Почему первый контроллер просмотра вращается назад, но не третий?
- Что нужно сделать, чтобы получить правильное поведение из ваших представлений, когда вы хотите, чтобы некоторые виды автоматически открывались, но не другие?
Cheers,
ЕР.
РЕДАКТИРОВАТЬ: В качестве крайней меры, прежде чем положить на нее щедрость, я полностью переписал этот вопрос, чтобы быть короче, яснее и, надеюсь, более привлекательным, чтобы дать ответ.
Ответы
Ответ 1
Короткий ответ заключается в том, что вы используете UINavigationController, и это не будет работать так, как вы этого хотите. Из документов Apple:
Почему мой UIViewController не будет вращаться вместе с устройством?
Все дочерние контроллеры в вашем UITabBarController или UINavigationController не согласен общий набор ориентации.
Чтобы убедиться, что все ваши дочерние элементы контроллеры вращаются правильно, вы должны воплощать в жизнь shouldAutorotateToInterfaceOrientation для каждого контроллера представления, представляющего каждой вкладке или уровне навигации. каждый должны согласиться с той же ориентацией для которые вращаются. То есть, они все должны вернуть YES для того же положения ориентации.
Подробнее о смотрите здесь вопросы о ротации.
Вам нужно будет свернуть собственный контроль стека/контроллера стека за то, что вы хотите сделать.
Ответ 2
Сделайте bolean в делетете приложения, чтобы контролировать, какую ориентацию вы хотите, например, сделать bool, чтобы включить Portrait и в вашем контроллере просмотра, который вы хотите разрешить Portrait, разрешить его совместно используемым приложением
в вашем контроллере просмотра, где вы хотите включить или отключить любую ориентацию, которую вы хотите.
((APPNAMEAppDelegate *)[[UIApplication sharedApplication] delegate]).enablePortrait= NO;
в App Delegate.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSLog(@"Interface orientations");
if(!enablePortrait)
return UIInterfaceOrientationMaskLandscape;
return UIInterfaceOrientationMaskLandscape|UIInterfaceOrientationMaskPortrait;
}
Этот метод будет запускаться каждый раз, когда вы поворачиваете устройство. На основе этих BOOL разрешите ориентацию, которую вы хотите.
Ответ 3
Был аналогичный вопрос несколько лет назад с рядом ответов. Вот недавний ответ от кого-то на этот вопрос:
Есть ли документальный способ установки ориентации iPhone?
Из того, что я понимаю, это проблема, которую испытывают многие люди, и в основном хаки - единственный способ ее исправить. Просмотрите эту тему, если вы ее раньше не видели и видите, работает ли что-нибудь для вас.
На стороне примечания, у меня была аналогичная проблема некоторое время назад, когда я вызывал что-то в shouldAutorotate, и я добавил код viewWillAppear
, чтобы попытаться его исправить. Я честно не помню, если это сработало, и у меня нет Mac больше, чтобы попробовать, но я нашел код, и я буду вставлять его здесь, если он дает какое-то вдохновение.
- (void)viewWillAppear:(BOOL)animated{
UIInterfaceOrientation o;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationPortrait:
o = UIInterfaceOrientationPortrait;
break;
case UIDeviceOrientationLandscapeLeft:
o = UIInterfaceOrientationLandscapeLeft;
break;
case UIDeviceOrientationLandscapeRight:
o = UIInterfaceOrientationLandscapeRight;
break;
default:
break;
}
[self shouldAutorotateToInterfaceOrientation:o];
}
Ответ 4
НЕ ИСПОЛЬЗУЙТЕ ЭТОТ ХАК, ЯБЛОК ОТКЛЮЧАЕТ ПРИЛОЖЕНИЕ, ОСНОВАННОЕ НА ИСПОЛЬЗОВАНИИ "ЧАСТНОГО API"
Для справки, я оставлю свой ответ здесь, но использование частного API не пройдет мимо обзорной доски. Сегодня я чему-то научился: D Как @younce правильно цитировал документы Apple, чего я не могу добиться с помощью UINavigationController.
У меня было два варианта. Во-первых, я мог бы написать свой собственный контроллер навигационного контроллера со всеми ужасами, с которыми он столкнулся при этом. Во-вторых, я мог бы взломать поворот в контроллерах представления, используя недокументированную функцию UIDevice под названием setOrientation:animated:
Потому что второй был соблазнительно легким, я пошел за этим. Это то, что я сделал. Вам понадобится категория для подавления предупреждений компилятора о том, что сеттер не существует:
@interface UIDevice (UndocumentedFeatures)
-(void)setOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated;
-(void)setOrientation:(UIInterfaceOrientation)orientation;
@end
Затем вам нужно проверить поддерживаемые ориентации на viewWillAppear:
. Рядом с используемыми здесь методами UIDevice вы можете также заставить портретную ориентацию представить модальный контроллер, но это произойдет мгновенно и не анимировано, так что это мой предпочтительный способ:
-(void)viewWillAppear:(BOOL)animated {
UIDevice *device = [UIDevice currentDevice];
UIDeviceOrientation realOrientation = device.orientation;
if ([self shouldAutorotateToInterfaceOrientation:realOrientation]) {
if (realOrientation != [UIApplication sharedApplication].statusBarOrientation) {
// Resetting the orientation will trigger the application to rotate
if ([device respondsToSelector:@selector(setOrientation:animated:)]) {
[device setOrientation:realOrientation animated:animated];
} else {
// Yes if Apple changes the implementation of this undocumented setter,
// we're back to square one.
}
}
} else if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
if ([device respondsToSelector:@selector(setOrientation:animated:)]) {
// Then set the desired orientation
[device setOrientation:UIDeviceOrientationPortrait animated:animated];
// And set the real orientation back, we don't want to truly mess with the iPhone balance system.
// Because the view does not rotate on this orientation, it won't influence the app visually.
[device setOrientation:realOrientation animated:animated];
}
}
}
Фокус в том, чтобы всегда поддерживать ориентацию внутреннего устройства в "реальной" ориентации устройства. Если вы начнете изменять это, вращение вашего приложения будет не в балансе.
Но, как я знаю сейчас, это просто верный способ отклонить ваше приложение. Поэтому вариант номер два - это просто плохой вариант. Перепишите, что NavigationController, или просто все ваши представления поддерживают один и тот же набор ориентации.
Cheers, EP.
Ответ 5
В iOS 6 это стало очень простой проблемой. Просто создайте специальный класс для просмотров, которые вы хотите авторотировать. Затем, в вашем rootVC, добавьте это.
-(BOOL)shouldAutorotate{
BOOL should = NO;
NSLog(@"%@", [self.viewControllers[self.viewControllers.count-1] class]);
if ([self.viewControllers[self.viewControllers.count-1] isKindOfClass:[YourAutorotatingClass class]]) {
should = YES;
}
return should;
}
Я знаю, что это старый вопрос, но я подумал, что стоит упомянуть.