IOS 6: Как ограничить некоторые взгляды портретом и позволить другим вращаться?

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

В iOS 5 я просто определил shouldAutorotateToInterfaceOrientation: в каждом из моих контроллеров представления, чтобы вернуть YES для допустимых ориентаций. Все работало, как описано выше, включая возврат к портрету, даже если устройство удерживалось в альбомной ориентации при возврате из контроллера # 4 на # 3.

В iOS 6 все контроллеры представлений вращаются в ландшафт, разбивая те, которые не были предназначены. В примечаниях к выпуску iOS 6 говорится:

Больше ответственности переместится в приложение и делегат приложения. Теперь контейнеры iOS (например, UINavigationController) не проконсультируют своих детей, чтобы определить, должны ли они авторотировать. [...] Система запрашивает самый полнофункциональный полноэкранный контроллер (обычно контроллер корневого представления) для поддерживаемых ориентаций интерфейса всякий раз, когда устройство вращается или всякий раз, когда контроллер представления представлен полноэкранным модальным стилем презентации. Более того, поддерживаемые ориентации извлекаются, только если этот контроллер представления возвращает YES из своего метода shouldAutorotate. [...] Система определяет, поддерживается ли ориентация путем пересечения значения, возвращаемого методом apps supportedInterfaceOrientationsForWindow: со значением, возвращаемым методом supportedInterfaceOrientations самого верхнего полноэкранного контроллера.

Итак, я подклассифицировал UINavigationController, дал мое MainNavigationController логическое свойство landscapeOK и использовал это, чтобы вернуть допустимые ориентации в supportedInterfaceOrientations. Затем в каждом из методов моего диспетчера просмотров viewWillAppear: у меня есть строка, подобная этой

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

чтобы сообщить мне MainNavigationController желаемое поведение.

Возникает вопрос: Если я сейчас перейду к четвертому виду в портретном режиме и поверните телефон, он повернется к пейзажу. Теперь я нажимаю кнопку "Назад", чтобы вернуться к третьему виду, которое должно работать только с портретом. Но он не вращается. Как мне сделать это?

Я пробовал

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

в методе viewWillAppear моего третьего контроллера представления, но он ничего не делает. Это неправильный метод для вызова или, возможно, неправильное место для его вызова или я должен полностью реализовать все это?

Ответ 1

У меня была такая же проблема, и я нашел решение, которое работает для меня. Чтобы он работал, недостаточно реализовать - (NSUInteger)supportedInterfaceOrientations в вашем UINavigationController. Вы также должны реализовать этот метод в своем контроллере № 3, который является первым, который должен быть только портретным после того, как вы выберете контроллер # 4. Итак, у меня есть следующий код в моем UINavigationController:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

В контроллере № 3 добавьте следующее:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

Вам не нужно добавлять что-либо к вашим контроллерам просмотров # 1, # 2 и # 4. Это работает для меня, я надеюсь, это поможет вам.

Ответ 2

Добавьте CustomNavigationController

Переопределите эти методы в нем:

-(BOOL)shouldAutorotate
{
    return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
    return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

Теперь добавьте все ориентации в plist

enter image description here

В диспетчере представлений добавьте только нужные:

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

эти методы переопределяют методы контроллера навигации

Ответ 3

Просматривая каждый ответ в бесчисленных похожих вопросах на SO, ни один из ответов не работал у меня, но они дали мне некоторые идеи. Вот как я решил решить проблему:

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

enter image description here

Затем создайте категорию UINavigationController (так как Apple заявляет, что не подклассифицирует ее):

@implementation UINavigationController (iOS6AutorotationFix)

-(BOOL)shouldAutorotate {
    return [self.topViewController shouldAutorotate];
}

@end

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

-(BOOL)shouldAutorotate {

    BOOL shouldRotate = NO;

    if ([navigationController.topViewController isMemberOfClass:[RotatingViewController class]] ) {
        shouldRotate = [navigationController.topViewController shouldAutorotate];
    }

    return shouldRotate;
}

Наконец, в RotatingViewController, выполните shouldAutorotate и supportedInterfaceOrientations следующим образом:

-(BOOL)shouldAutorotate {
    // Preparations to rotate view go here
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAllButUpsideDown; // or however you want to rotate
}

Причина, по которой вам нужно это сделать, - это то, что iOS 6 дает управление вращением в корневом контроллере вместо верхнего элемента управления. Если вы хотите, чтобы вращение отдельного вида велось иначе, чем другие представления в стеке, вам нужно написать конкретный случай для него в контроллере корневого представления.

Ответ 4

У меня недостаточно репутации, чтобы комментировать ответ @Brian, поэтому я добавлю здесь свою заметку.

Брайан упомянул, что iOS6 предоставляет управление поворотом для rootViewController - это может быть не только UINavigationController, как упоминалось, но и UITabBarController, который он для меня. Моя структура выглядит так:
  • UITabBarController
    • UINavigationController
      • UIViewControllers...
    • UINavigationController
      • UIViewControllers...

Итак, я сначала добавил методы в пользовательский UITabBarController, затем в пользовательский UINavigationController, а затем, наконец, в конкретный UIViewController.

Пример из UITabBarController и UINavigationController:

- (BOOL)shouldAutorotate {
    return [self.viewControllers.lastObject shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.viewControllers.lastObject supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.viewControllers.lastObject shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];
}

Ответ 5

Я хотел бы дать частичный ответ на мой собственный вопрос. Я нашел следующую строку кода, используемую в методе viewWillAppear моего третьего UIViewController, для работы:

[[UIDevice currentDevice] 
      performSelector:NSSelectorFromString(@"setOrientation:") 
           withObject:(id)UIInterfaceOrientationPortrait];

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

Мне очень хочется оставить это в своем приложении, поскольку он просто работает. Но это не так, поэтому я хотел бы оставить вопрос открытым для чистого решения.

Ответ 6

Это я использую для поддержки ориентации в ios 6.0

-(BOOL)shouldAutorotate{
    return YES;
}  

 - (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskAll;
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{  
  return UIInterfaceOrientationPortrait;
}

Ответ 7

То, что это очень просматриваемый поток. Я думал, что добавлю, что, по-моему, самый легкий ответ. Это работает для ios8 и up

-(BOOL)shouldAutorotate
{
    return YES;
}

и

-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

Что это. Наслаждайтесь!

О, и мои ViewControllers встроены в контроллер навигации, который мне не нужно подклассировать или настроить каким-либо образом.

Ответ 8

Вы хотите принудительно создать порт приложения iOS 6, после чего вы можете добавить в подкласс UIViewController ниже методы

- (BOOL)shouldAutorotate {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    } else {
        return NO;
    }
}


- (NSUInteger)supportedInterfaceOrientations {
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return UIInterfaceOrientationMaskAll;
    } else {
        return UIInterfaceOrientationMaskPortrait;
    }
}

Ответ 9

Это может не сработать для всех, но это отлично работает для меня. Вместо реализации...

[(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

в viewWillAppear во всех моих контроллерах, я решил централизовать этот процесс внутри моего подкласса UINavigationController, переопределив метод UINavigationControllerDelegate navigationController:willShowViewController:animated:

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

    self.previousVCLandscapeOK = self.isLandscapeOK; // Store the current VC orientation preference before pushing on the new VC so we can set this again from within the custom "back" method
    self.isLandscapeOK = NO; // Set NO as default for all VC's
    if ([viewController isKindOfClass:[YourViewController class]]) {
        self.isLandscapeOK = YES;
    }
}

Я обнаружил, что этот метод делегата не вызывается при выводе VC из навигационного стека. Это не было проблемой для меня, потому что я обрабатываю функции back из моего подкласса UINavigationController, чтобы я мог настроить правильные кнопки и действия панели навигации для конкретного VC, как это...

if ([viewController isKindOfClass:[ShareViewController class]]) {

    UIButton* backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 57, 30)];
    [backButton setImage:[UIImage imageNamed:@"back-arrow"] forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem* backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
    viewController.navigationItem.leftBarButtonItem = backButtonItem;

    UIImageView* shareTitle = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"share-title"]];
    [shareTitle setContentMode:UIViewContentModeScaleAspectFit];
    [shareTitle setFrame:CGRectMake(0, 0, shareTitle.frame.size.width - 10, shareTitle.frame.size.height - 10)];
    viewController.navigationItem.titleView = shareTitle;

} else if(...) {
    ...
}

Вот что мой метод back выглядит так, чтобы обрабатывать выскакивание VC от стека и устанавливать соответствующие предпочтения вращения...

- (void)back {
    self.isLandscapeOK = self.previousVCLandscapeOK;
    self.previousVCLandscapeOK = NO;
    [self popViewControllerAnimated:YES];
}

Итак, как вы можете видеть, в основном все, что происходит, я сначала задаю мне два свойства...

@property (nonatomic) BOOL isLandscapeOK;
@property (nonatomic) BOOL previousVCLandscapeOK;

в navigationController:willShowViewController:animated:, который определит, какие поддерживаемые ориентации находятся внутри этого VC, который должен быть представлен. При появлении VC, мой пользовательский метод "назад" вызывается, и затем я устанавливаю isLandscapeOK в то, что было сохранено с помощью значения предыдущегоVCLandscapeOK.

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

Надеюсь, это помогает кому-то, как я. Спасибо, Джереми.

Ответ 10

Перейдите к файлу Info.plist и внесите изменения enter image description here

Ответ 11

Я хотел, чтобы все мои VC были заблокированы в портретной ориентации, кроме одного. Это то, что сработало для меня.

  • Добавить поддержку всех ориентаций в файле plist.
  • В контроллере корневого представления определите тип контроллера вида, который находится поверх окна, и установите ориентацию приложения соответствующим образом в методе supportedInterfaceOrientations. Например, мне нужно, чтобы мое приложение вращалось только тогда, когда веб-просмотр находился поверх стека. Вот что я добавил в свой rootVC:

    -(NSUInteger)supportedInterfaceOrientations
    {
        UIViewController *topMostViewController = [[Utils getAppDelegate] appNavigationController].topViewController;
        if ([topMostViewController isKindOfClass:[SVWebViewController class]]) {
            return UIInterfaceOrientationMaskAllButUpsideDown;
        }
        return UIInterfaceOrientationMaskPortrait;
    }
    

Ответ 12

У меня недостаточно репутации, чтобы ответить на вопрос @Ram S в ответ на @micmdk, поэтому я добавлю здесь свою заметку.

Когда вы используете UITabbarController, попробуйте изменить self.viewControllers.lastObject в @micmdk код для self.selectedViewController следующим образом:

- (BOOL)shouldAutorotate {
    return [self.selectedViewController shouldAutorotate];
}

- (NSUInteger)supportedInterfaceOrientations {
    return [self.selectedViewController supportedInterfaceOrientations];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return [self.selectedViewController shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}

Ответ 13

Я решил такую же проблему.

Если вы используете UINavigationController для нажатия на контроллеры представления, вам необходимо установить методы ниже.

extension UINavigationController{

    override open var shouldAutorotate: Bool {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return true
        }
        return false
    }

    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight

    }
    override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {

        if topViewController != nil && (topViewController?.isKind(of: LogInViewController.self))!
        {
            return .portrait
        }
        return .landscapeRight
    }
}

LoginViewController UIViewController вы LoginViewController использовать UIViewController. В моем случае, я хочу, чтобы показать LoginViewController в Portrait режим других ViewControllers в landscape режиме.

Ответ 14

Вы можете реализовать и переопределить shouldAutorotate() и supportedInterfaceOrientations var во всех классах View Controller, которые должны быть представлены в разных ориентациях, чем те, которые определены в PLIST вашего приложения.

Однако в нетривиальном пользовательском интерфейсе вы можете столкнуться с проблемой, чтобы добавить его в десятки классов, и вы не хотите, чтобы все из них были подклассом нескольких общих, которые его поддерживают (MyBaseTableViewController, MyBaseNavigationController и MyBaseTabBarController).

Поскольку вы не можете переопределить эти методы /var на UIViewController напрямую, вы можете сделать это в своих подклассах, которые обычно являются вашими базовыми классами, такими как UITableViewController, UINavigationController и UITabBarController.

Таким образом, вы можете реализовать несколько расширений и по-прежнему настроить MyPreciousViewController для отображения в разных ориентациях, чем все остальные, подобные этому фрагменту кода Swift 4:

extension UITableViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    if let last = self.navigationController?.childViewControllers.last,
        last != self {
            return last.supportedInterfaceOrientations
    } else {
        return [.portrait]
    }
    }
}

extension MyPreciousViewController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

    return [.portrait,.landscape]
    }
}


extension UINavigationController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}


extension UITabBarController {
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {

        return [.portrait]
    }
}

Ответ 15

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

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

вернуть только желаемую ориентацию!!!