Как представить контроллер просмотра в портретном или ландшафтном режиме, в приложении, предназначенном только для портрета

У меня есть приложение для iPhone, большинство из которых только для портрета, но с одним контроллером представления (экран видеоплеера), который должен поддерживать как портрет, так и пейзаж (это только для iOS 8). Чтобы достичь этого, я установил приложение plist для поддержки портрета и обоих видов ландшафта, а затем подкласса UINavigationController; в этом подклассе я переопределяю

- (BOOL)shouldAutorotate {
    return YES;
}

и

- (NSUInteger)supportedInterfaceOrientations {

    if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]]) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }

}

Это в основном работает так, как ожидалось: начальные экраны - только портретные и остаются в портрете, даже когда устройство повернуто к пейзажу. Видеопроигрыватель при первоначальном представлении:

MyVideoPlayerScreen *newVC = [MyVideoPlayerScreen new];
[self.navigationController pushViewController:newVC animated:YES];

также находится в портрете, но затем поворачивается к пейзажу, когда устройство повернуто - все хорошо до сих пор.

Проблема заключается в том, что если я превращу устройство в альбомное, видеопроигрыватель тоже будет ландшафтным, но затем, когда я уберу экран видеопроигрывателя с помощью кнопки "Назад", основной контроллер представления (который должен быть только портретным ) теперь также в ландшафте. Если я верну устройство обратно к портрету, контроллер просмотра снова вернется к портрету, и затем он будет правильно зафиксирован только в портретном режиме с этой точки.

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

Этот вопрос задан в миллион раз, но, похоже, исправления, которые были отправлены для него, - это все хаки, которые больше не работают в iOS 8.

Обновление: Я нашел исправление типа для этого, которое работает в iOS 8. В моем подклассе UINavigationController я обрабатываю протокол <UINavigationControllerDelegate>. Я применил этот метод:

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {

    if (![viewController isKindOfClass:[MyVideoPlayerScreen class]]) {

        // if the current orientation is not already portrait, we need this hack in order to set the root back to portrait
        UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
        if (orientation != UIInterfaceOrientationPortrait) {

            // HACK: setting the root view controller to nil and back again "resets" the navigation bar to the correct orientation
            UIWindow *window = [[UIApplication sharedApplication] keyWindow];
            UIViewController *vc = window.rootViewController;
            window.rootViewController = nil;
            window.rootViewController = vc;

        }

    }

}

Это, по крайней мере, оставляет пользовательский интерфейс в состоянии, в котором я хочу, чтобы он находился. Проблема в том, что когда диспетчер представлений верхнего уровня уволен и анимирован за кадром, основной контроллер представления все еще находится в ландшафте; затем он внезапно перескакивает на портрет. Не идеально, но лучше, чем ничего.

Обновление 2: Я должен был добавить, что я нажимаю второй контроллер представлений следующим образом:

ViewControllerPortraitLandscape *newVC = [ViewControllerPortraitLandscape new];
[self.navigationController pushViewController:newVC animated:YES];

Обновление 3: ОК, это вполне выполнимо, что работает для iOS 6 и выше. Исправление даже имеет смысл, хотя причина его работы, похоже, не в документации. В принципе, в контроллере представления вам нужно быть reset на портрете, когда диспетчер представлений верхнего уровня отклоняется, пока устройство по-прежнему находится в ландшафте, вам просто нужно добавить это:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

Хотя мой подкласс UINavigationController полностью отвечает за вызовы вращения, точки останова показывают, что supportedInterfaceOrientations вызывается на базовом контроллере представления перед тем, как высший уровень отклоняется, и он вызвал контроллер навигации после верхнего уровня -level отклоняется. Поэтому я предполагаю, что этот вызов самому контроллеру представления сделан iOS, чтобы определить, на какую ориентацию должен находиться основной контроллер представления (и для этого он не запрашивает навигационный контроллер); если он явно не переопределен, он вернет параметр all-but-upside-down, поэтому iOS просто оставляет его там, где он есть, в ландшафте.

Ответ 1

Оказывается, это простое решение: вы можете управлять всем процессом через подкласс UINavigationController, как я разместил здесь (т.е. не внедрять ни один из этих методов вращения в самих контроллерах представления), за исключением того, что для любого контроллера вида который должен быть только портретным, вам также необходимо реализовать это:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

С точками останова вы можете видеть, что этот метод вызывается до того, как выше установленный контроллер просмотра выслан; iOS, по-видимому, использует этот вызов для определения того, в какой ориентации должен находиться "обнаруженный" контроллер представления (тот же вызов подкласса контроллера навигации вызывается после того, как диспетчер представления верхнего уровня отклонен).

С помощью этого метода, реализованного в контроллере представления, все работает так, как ожидалось.

Ответ 2

Возьмите портрет EXCEPT при представлении отдельного подкласса UIViewController. Эта статья очень помогла: http://www.fantageek.com/1085/force-only-one-view-controller-to-be-landscape/

установите для Info.plist поддержку, портрет, пейзаж влево, пейзаж справа

реализовать приложение: поддерживаетсяInterfaceOrientationsForWindow: например:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
// (I specifically want landscape left for the movie viewing)

подкласс UINavigationController (root's rootController) и переопределяет его так:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait; // enforces the "portrait everything" requirement
}

Наконец, я должен был убедиться, что пользовательский контроллер просмотра игроков "переопределит" поддерживаемую ориентацию:

- (BOOL)shouldAutorotate
{
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return UIInterfaceOrientationLandscapeLeft;
}

Результат: Подкласс UIViewController представляет, но при отклонении пользовательского контроллера представления контроллер представления представлений представляет собой портрет.

Из статьи fantageek: "Система пересекает поддерживаемые ориентацией представления контроллеры с ориентациями, поддерживаемыми приложениями (как определено в файле Info.plist или приложении для делегатов приложения: supportedInterfaceOrientationsForWindow: method), чтобы определить, нужно ли вращаться".

Ответ 3

Думаю, вам следует изменить это и повторить попытку

-(NSUInteger)supportedInterfaceOrientations {
 if ([self.visibleViewController isKindOfClass:[MyVideoPlayerScreen class]]) 
 {
    return UIInterfaceOrientationMaskLandscape;
 } 
 else 
 {
    return UIInterfaceOrientationMaskPortrait;
 }
 }

Ответ 4

Вот как вы это делаете.

Внедрите эти 3 метода как для представления, так и для представленного контроллера:

- (BOOL)shouldAutorotate {
    return NO; //-- for presented controller use YES
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscape; //-- any orientation you need
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

и теперь вы можете использовать в контроллере представления:

[self presentViewController:presentedController animated:true completion:nil];

Таким образом, когда вы вернетесь к представителю контроллера, он будет иметь правильную ориентацию.

Ответ 5

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