design - Como forçar a orientação do controlador de exibição no iOS 8?



search ios (16)

Minha solução

No AppDelegate :

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if let topController = UIViewController.topMostViewController() {
        if topController is XXViewController {
            return [.Portrait, .LandscapeLeft]
        }
    }
    return [.Portrait]
}

XXViewController é o ViewController que você deseja suportar o modo Paisagem.

Então a solução de Sunny Shah funcionaria no seu XXViewController em qualquer versão do iOS:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

Esta é a função de utilidade para encontrar o ViewController mais alto.

extension UIViewController {

    /// Returns the current application's top most view controller.
    public class func topMostViewController() -> UIViewController? {
        let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
        return self.topMostViewControllerOfViewController(rootViewController)
    }



    /// Returns the top most view controller from given view controller's stack.
    class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
        // UITabBarController
        if let tabBarController = viewController as? UITabBarController,
           let selectedViewController = tabBarController.selectedViewController {
            return self.topMostViewControllerOfViewController(selectedViewController)
        }

        // UINavigationController
        if let navigationController = viewController as? UINavigationController,
           let visibleViewController = navigationController.visibleViewController {
            return self.topMostViewControllerOfViewController(visibleViewController)
        }

        // presented view controller
        if let presentedViewController = viewController?.presentedViewController {
            return self.topMostViewControllerOfViewController(presentedViewController)
        }

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
            if let childViewController = subview.nextResponder() as? UIViewController {
                return self.topMostViewControllerOfViewController(childViewController)
            }
        }

        return viewController
    }

}

Antes do iOS 8, usamos o código abaixo em conjunto com supportedInterfaceOrientations e shouldAutoRotate para delegar métodos para forçar a orientação do aplicativo para qualquer orientação específica. Usei abaixo o trecho de código para girar o aplicativo programaticamente para a orientação desejada. Em primeiro lugar, estou alterando a orientação da barra de status. E então, apenas apresentando e descartando imediatamente uma visão modal, a visão é rotacionada para a orientação desejada.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Mas isso está falhando no iOS 8. Além disso, tenho visto algumas respostas no estouro de pilha, onde as pessoas sugeriram que sempre devêssemos evitar essa abordagem do iOS 8 em diante.

Para ser mais específico, meu aplicativo é um tipo universal de aplicativo. Existem três controladores no total.

  1. Controlador First View - Deve suportar todas as orientações no iPad e apenas o retrato (botão home para baixo) no iPhone.

  2. Controlador Second View - Deve suportar apenas a paisagem correta em todas as condições

  3. Controlador da terceira vista - Deve suportar apenas a paisagem certa em todas as condições

Estamos usando o controlador de navegação para navegação de página. Do primeiro controlador de visão, em uma ação de clique de botão, estamos empurrando o segundo na pilha. Assim, quando o segundo controlador de exibição chega, independentemente da orientação do dispositivo, o aplicativo deve bloquear apenas a paisagem.

Abaixo estão meus métodos shouldAutorotate e supportedInterfaceOrientations no segundo e terceiro controlador de visualização.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Existe alguma solução para esta ou qualquer outra maneira melhor de bloquear um controlador de visualização em orientação específica para o iOS 8. Por favor, ajude !!


Answer #1

A combinação de respostas de Sids e Koreys funcionou para mim.

Estendendo o Controlador de Navegação:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

Então desabilitando a rotação na visão única

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

E girando para a orientação apropriada antes da sequência

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}

Answer #2

Ainda parece haver algum debate sobre a melhor forma de realizar essa tarefa, então pensei em compartilhar minha abordagem (de trabalho). Adicione o seguinte código em sua implementação do UIViewController :

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

Para este exemplo, você também precisará definir suas orientações de dispositivo permitidas para 'Paisagem à esquerda' nas configurações do projeto (ou diretamente em info.plist ). Basta alterar a orientação específica que você deseja forçar se quiser algo diferente de LandscapeLeft.

A chave para mim foi a attemptRotationToDeviceOrientationchamada viewWillAppear- sem que a vista não girasse corretamente sem girar fisicamente o dispositivo.


Answer #3

De acordo com a resposta de Korey Hinton

Swift 2.2:

extension UINavigationController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return visibleViewController!.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        return visibleViewController!.shouldAutorotate()
    }
}


extension UITabBarController {
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations()
        }
        return super.supportedInterfaceOrientations()
    }
    public override func shouldAutorotate() -> Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate()
        }
        return super.shouldAutorotate()
    }
}

Desativar rotação

override func shouldAutorotate() -> Bool {
    return false
}

Bloquear para orientação específica

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}

Answer #4

Descobri que, se é um controlador de visualização apresentado, você pode substituir preferredInterfaceOrientationForPresentation

Rápido:

override func supportedInterfaceOrientations() -> Int {
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
  return UIInterfaceOrientation.LandscapeLeft
}

override func shouldAutorotate() -> Bool {
  return false
}

Answer #5

Desta forma trabalhe para mim no Swift 2 iOS 8.x:

PS (este método não requer a sobreposição de funções de orientação como shouldautorotate em cada viewController, apenas um método em AppDelegate)

Marque a opção "requer tela cheia" em suas informações gerais do projeto.

Então, no AppDelegate.swift, faça uma variável:

var enableAllOrientation = false

Então, coloque também essa func:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if (enableAllOrientation == true){
            return UIInterfaceOrientationMask.All
        }
        return UIInterfaceOrientationMask.Portrait
}

Portanto, em todas as classes do seu projeto, você pode definir esse var no viewWillAppear:

override func viewWillAppear(animated: Bool)
{
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        appDelegate.enableAllOrientation = true
}

Se você precisar fazer escolhas com base no tipo de dispositivo, poderá fazer isso:

override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        switch UIDevice.currentDevice().userInterfaceIdiom {
        case .Phone:
        // It's an iPhone
           print(" - Only portrait mode to iPhone")
           appDelegate.enableAllOrientation = false
        case .Pad:
        // It's an iPad
           print(" - All orientation mode enabled on iPad")
           appDelegate.enableAllOrientation = true
        case .Unspecified:
        // Uh, oh! What could it be?
           appDelegate.enableAllOrientation = false
        }
    }

Answer #6

Eu tentei algumas soluções aqui e o importante é entender que é o controlador de visão raiz que determinará se ele girará ou não.

Eu criei o seguinte projeto github.com/GabLeRoux/RotationLockInTabbedViewChild -c github.com/GabLeRoux/RotationLockInTabbedViewChild com um exemplo de funcionamento de um TabbedViewController onde uma visão filho é permitida rotacionar e a outra visão filho é bloqueada em retrato.

Não é perfeito, mas funciona e a mesma ideia deve funcionar para outro tipo de visualização de raiz, como o NavigationViewController . :)


Answer #7

Eu tentei muitas soluções, mas o que funcionou é o seguinte:

Não há necessidade de editar o info.plist no ios 8 e 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
}

Orientações possíveis da documentação da Apple :

UIInterfaceOrientationUnknown

A orientação do dispositivo não pode ser determinada.

UIInterfaceOrientationPortrait

O dispositivo está no modo retrato, com o dispositivo na vertical e o botão inicial na parte inferior.

UIInterfaceOrientationPortraitUpsideDown

O dispositivo está no modo retrato, mas está de cabeça para baixo, com o dispositivo na vertical e o botão inicial na parte superior.

UIInterfaceOrientationLandscapeLeft

O dispositivo está no modo paisagem, com o dispositivo na vertical e o botão inicial no lado esquerdo.

UIInterfaceOrientationLandscapeRight

O dispositivo está no modo paisagem, com o dispositivo na vertical e o botão inicial no lado direito.



Answer #9

Meus requisitos são

  1. bloquear todas as vistas no modo retrato
  2. use AVPlayerViewController para reproduzir vídeo

Quando o vídeo está sendo reproduzido, se for uma paisagem, permita que a tela gire a paisagem para a direita e a paisagem para a esquerda. Se for um retrato, bloqueie a vista apenas no modo retrato.

Primeiro, defina supportedInterfaceOrientationsForWindow no AppDelegate.swift

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if portrait {
        return .Portrait
    } else {
        return .Landscape
    }
}

Em segundo lugar, no seu controlador de visualização principal, defina as seguintes funções

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    print("\(#function)")
    return .Portrait
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return .Portrait
}

override func shouldAutorotate() -> Bool {
    return false
}

Então, você precisa subclassificar AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController {

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let size = size {
            if size.width > size.height {
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
            } else {
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            }
        }
    }

Substitua essas três funções em MyPlayerViewController.swift

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return self.supportedOrientationMask!
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return self.preferredOrientation!
}

Como o usuário pode girar a paisagem do dispositivo para a esquerda ou a paisagem para a direita, precisamos definir a rotação automática como verdadeira

override func shouldAutorotate() -> Bool {
    return true
}

Por fim, crie a instância MyPlayerViewController no controlador de exibição e defina o valor do tamanho da propriedade.

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

Inicie seu player com o videoUrl adequado e atribua seu player ao playerViewController . Codificação feliz!


Answer #10

Para iOS 7 - 10:

Objetivo-C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Swift 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

Basta chamá-lo em - viewDidAppear: do controlador de visualização apresentado.


Answer #11

Primeiro de tudo - esta é uma má idéia, em geral, algo de errado com a arquitetura do seu aplicativo, mas, sh..t happens , se assim for, você pode tentar fazer algo como abaixo:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Do que, no AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

E sempre que você quiser mudar a orientação, faça o seguinte:

OrientationController.forceLockOrientation(.landscapeRight)

Nota: Às vezes, o dispositivo não pode atualizar de tal chamada, então você pode precisar fazer o seguinte

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

Isso é tudo


Answer #12

Se você estiver usando navigationViewController você deve criar sua própria superclasse para isso e substituir:

- (BOOL)shouldAutorotate {
  id currentViewController = self.topViewController;

  if ([currentViewController isKindOfClass:[SecondViewController class]])
    return NO;

  return YES;
}

Isso desabilitará a rotação no SecondViewController, mas se você pressionar o seu SecondViewController quando o dispositivo estiver na orientação vertical, seu SecondViewController aparecerá no modo retrato.

Suponha que você esteja usando o storyboard. Você tem que criar segue manual ( como ) e em seu método "onClick":

- (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
  NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
  [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
  [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
}

Isso forçará a orientação de paisagem antes que a superclasse desative o recurso de autorotação.


Answer #13

Use isso para bloquear a orientação do controlador de visualização testado no IOS 9:

// Travar orientação para a paisagem direita

-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
    return UIInterfaceOrientationMaskLandscapeRight;
}

Answer #14

Eu tenho o mesmo problema e desperdiço muito tempo para isso. Então agora eu tenho a minha solução. Minha configuração de aplicativo é apenas retrato de apoio. No entanto, algumas telas do meu aplicativo precisam ter apenas paisagem.Eu corrigi-lo por ter uma variável isShouldRotate no AppDelegate . E a função no AppDelegate :

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

E finalmente quando um ViewControllerA precisa de um estado de paisagem. Basta fazer isso: antes de push / presente para ViewControllerA atribuir isShouldRotate para true . Não se esqueça de quando pop / dismiss que o controlador designar isShouldRotate para false em viewWillDisappear .


Answer #15

Para mim, o nível superior VC precisava implementar as sobreposições de orientação. Usar o VC na pilha não terá efeito se o topo do VC não estiver implementando.

VC-main
    |
    -> VC 2
        |
        -> VC 3

Apenas o VC-Main é ouvido, essencialmente nos meus testes.





autorotate