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

149

Antes do iOS 8, usamos o código abaixo em conjunto com os métodos de delegação supportedInterfaceOrientations e shouldAutoRotate para forçar a orientação do aplicativo a qualquer orientação específica. Usei o trecho de código abaixo para girar programaticamente o aplicativo para a orientação desejada. Em primeiro lugar, estou alterando a orientação da barra de status. E então, apenas apresentar e descartar imediatamente uma vista modal gira a vista 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, nas quais as pessoas sugeriam que devemos sempre evitar essa abordagem a partir do iOS 8.

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

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

  2. Controlador Second View - Ele deve suportar apenas paisagem em todas as condições

  3. Controlador Third View - ele deve suportar apenas paisagem em todas as condições

Estamos usando o controlador de navegação para navegação na página. No primeiro controlador de exibição, em uma ação de clique no botão, estamos pressionando o segundo na pilha. Portanto, quando o segundo controlador de exibição chegar, independentemente da orientação do dispositivo, o aplicativo deverá bloquear apenas a paisagem.

Abaixo estão meus métodos shouldAutorotatee supportedInterfaceOrientationsno segundo e terceiro controlador de exibição.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Existe alguma solução para essa ou qualquer maneira melhor de bloquear um controlador de exibição em particular orientação para o iOS 8. Por favor, ajude !!

Rashmi Ranjan mallick
fonte
A implementação dos métodos mencionados no VC apresentado geralmente deve funcionar (pelo menos essa é a minha experiência no iOS 8). Talvez sua configuração específica esteja causando problemas?
Vladimir Gritsenko 14/10
Talvez eu possa deixar a pergunta um pouco mais clara. Vou editar a pergunta um pouco.
Rashmi Ranjan mallick
@VladimirGritsenko: Por favor, verifique agora. Eu editei.
Rashmi Ranjan mallick
O código na pergunta não usa a pilha do controlador de navegação, mas a apresentação modal, portanto ainda não está claro o que você está fazendo exatamente. Eu direi que em nosso código, retornar YES de shouldAutoRotate e retornar as orientações desejadas de supportedInterfaceOrientations no VC apresentado orientam adequadamente esse VC.
Vladimir Gritsenko 14/10
Bem, não é realmente uma falha, é apenas uma grande mudança de conceito. Se é uma boa mudança é outro assunto.
Kegluneq #

Respostas:

315

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 no - viewDidAppear:controlador de exibição apresentado.

Sunny Shah
fonte
61
Esta não é uma API privada. Esse é um uso não seguro de APIs. Você não está usando nenhum método privado, mas usando 'coisas internas'. Se a Apple alterar o valor de "orientação", ele falhará. Pior: se a Apple alterar o significado do valor de "orientação", você não saberá o que está alterando ao definir o valor.
sonxurxo
4
Para desativar a animação, colocar [UIView setAnimationsEnabled:NO];em viewWillAppeare [UIView setAnimationsEnabled:YES];em viewDidAppear, relacionado: stackoverflow.com/a/6292506/88597
ohho
3
Bom corte, mas o problema com isso, é que não está disparando a didRotateFromInterfaceOrientation depois de definir o novo valor de orientação (suspiro)
loretoparisi
2
No iOS 9.2, isso muda apenas a orientação, mas não está bloqueando.
Mark13426
2
Para todos tendo este se comportam bom 90% do tempo e carrinho os outros 10% do tempo, chamar este depois de definir a chave de orientação:[UINavigationController attemptRotationToDeviceOrientation];
Albert Renshaw
93

A rotação da orientação é um pouco mais complicada se você estiver dentro de um UINavigationControllerou UITabBarController. O problema é que, se um controlador de exibição estiver incorporado em um desses controladores, o controlador da barra de navegação ou de tabulação terá precedência e tomará decisões sobre autorotação e orientações suportadas.

Eu uso as 2 extensões a seguir no UINavigationController e UITabBarController para que os controladores incorporados em um desses controladores tomem as decisões.

Dê aos controladores de exibição o poder!

Swift 2.3

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

extension UITabBarController {
    public override func supportedInterfaceOrientations() -> Int {
        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()
    }
}

Swift 3

extension UINavigationController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
    }
}

extension UITabBarController {
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if let selected = selectedViewController {
            return selected.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }

    open override var shouldAutorotate: Bool {
        if let selected = selectedViewController {
            return selected.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

Agora você pode substituir o supportedInterfaceOrientationsmétodo ou pode substituirshouldAutoRotate no controlador de exibição que deseja bloquear, caso contrário, pode deixar de fora as substituições em outros controladores de exibição que deseja herdar o comportamento de orientação padrão especificado na lista de aplicativos do seu aplicativo

Desativar rotação

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

Bloquear a orientação específica

class ViewController: UIViewController {
    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
}

Em teoria, isso deve funcionar para todas as hierarquias complexas de controladores de exibição, mas notei um problema com o UITabBarController. Por alguma razão, ele deseja usar um valor de orientação padrão. Consulte a seguinte postagem no blog se estiver interessado em aprender sobre como solucionar alguns dos problemas:

Bloquear rotação da tela

Korey Hinton
fonte
1
Eu sei que este é um post muito antigo, mas onde você colocaria o código de extensão?
dwinnbrown
7
A marcação para uma resposta Swift apenas em uma pergunta Objective-C.
Charlie Scott-Skinner
17
Esta é uma boa solução para Swift e ObjC. Não entendo por que as pessoas não votam. Você entendeu a ideia e, em seguida, escrever código é fácil. Por que reclamar?
Zhao
2
@ Krodak, Nas extensões, tente alterar "-> Int" para "-> UIInterfaceOrientationMask". Fiz o truque para mim.
The_pantless_coder # 10/16
2
A idéia e o código são perfeitos, eu adoro isso, mas há um problema ao usar o UINavigationController e retornar de um UIViewController exibido em paisagem para um que deve ser exibido em retrato. Alguma ideia?
crisisGriega
24

Isto é o que funcionou para mim:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

Chame-o no seu método viewDidAppear:.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];
}
Vincil Bishop
fonte
3
Muito útil, se o dispositivo já é girado e você precisa rodar o seu controlador de exibição
avdyushin
2
Ótima resposta. Quando usado em combinação com a resposta aceita, funciona sempre para mim no Swift e no iOS 9+.
AndyDunn
1
Embora eu ache que não seja a resposta para a pergunta, mas realmente me ajudou.
Abdurrahman Mubeen Ali
21

Descobri que se é um controlador de exibiçã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
}
Zipme
fonte
4
Parece que a resposta aceita simplesmente desencadeia uma mudança de orientação para paisagem. O que você mostrou aqui é como forçar um controlador de exibição a ficar apenas na paisagem e não afetar o controlador de exibição de apresentação (que é exatamente o que eu preciso). Obrigado.
Clifton Labrum 04/04
1
@CliftonLabrum você fez mais alguma coisa para forçar apenas a paisagem? Usando o mesmo código, o controlador de exibição ainda pode girar para outra orientação.
Crashalot
Mais tarde eu percebi que - você está correto. Ainda não encontrei uma correção.
Clifton Labrum
1
Isso funcionou para mim. Consegui manter um único View Controller no modo Retrato usando esse código (substituindo Paisagem por Retrato). +1
Mark Barrasso
Sim! Isso funcionou para mim também no Xcode 7.3 & Swift 2.2. Mas ainda é necessário definir o aplicativo func para supportedInterfaceOrientationsForWindow no AppDelegate.
Charles.cc.hsu
15

Dessa forma, funciona para mim no Swift 2 iOS 8.x:

PS (esse método não requer que as funções de orientação sejam substituídas, como deve-se fazer a autenticação em todos os viewController, apenas um método no AppDelegate)

Marque a opção "requer tela cheia" no projeto de informações gerais. insira a descrição da imagem aqui

Portanto, no AppDelegate.swift, faça uma variável:

var enableAllOrientation = false

Então, coloque também esta função:

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 em 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, faça o seguinte:

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
        }
    }
Alessandro Ornano
fonte
muito bom explicação
Mihir Oza
11

Primeiro de tudo - essa é uma péssima idéia, em geral, algo de errado com a arquitetura do seu aplicativo, mas, sh..t happensnesse caso, você pode tentar criar 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, em 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 alterar a orientação, faça o seguinte:

OrientationController.forceLockOrientation(.landscapeRight)

Nota: Às vezes, o dispositivo pode não ser atualizado a partir dessa chamada, portanto, você pode fazer o seguinte

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

Isso é tudo

gbk
fonte
9

Isso deve funcionar no iOS 6 em diante, mas eu o testei apenas no iOS 8. Subclasse UINavigationControllere substitua os seguintes métodos:

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

- (BOOL)shouldAutorotate {
    return NO;
}

Ou pergunte ao controlador de vista visível

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}

- (BOOL)shouldAutorotate {
    return self.visibleViewController.shouldAutorotate;
}

e implementar os métodos lá.

Mojo66
fonte
Acabei de testar isso no meu aplicativo no Xcode 8.2.1 executando o iOS 10.0 e funciona. O menu do meu jogo é a primeira tela e não gira; todas as outras vistas são rotacionadas. Perfeito! Deu um presente. Eu só precisava da função 'selectedInterfaceOrientationForPresentation' fornecida por Mojo66.
Philip Borges
9

Este é um feedback para os comentários na resposta de Sid Shah , sobre como desativar animações usando:

[UIView setAnimationsEnabled:enabled];

Código:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // Stackoverflow #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];
}
Oh Ho
fonte
Como posso usar isso para o Portrait.
Muju
6

Se você estiver usando o navigationViewController, crie sua própria superclasse para isso e substitua:

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

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

  return YES;
}

isso desativará a rotação no SecondViewController, mas se você pressionar o SecondViewController quando o dispositivo estiver na orientação retrato, o SecondViewController aparecerá no modo retrato.

Suponha que você esteja usando o storyboard. Você precisa criar segue manual ( Como fazer ) e no 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 da paisagem antes que sua superclasse desative o recurso de rotação automática.

Lau
fonte
6

Nos Xcode 8métodos são convertidos em propriedades, portanto, o seguinte funciona com Swift:

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}
Ali Momen Sani
fonte
Isso bloqueia a orientação?
precisa saber é o seguinte
1
@bnussey Eu queria impedir a rotação automática e estar sempre no portraitmodo, independentemente da orientação no iPad, com o shouldAutorotateretorno verdadeiro, consegui isso.
Ali Momen Sani
4

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

Não é necessário editar o info.plistios 8 e 9.

- (BOOL) shouldAutorotate {
    return NO;
}   

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

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

UIInterfaceOrientationNão conhecido

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 de cabeça para baixo, com o dispositivo na vertical e o botão home 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.

hoogw
fonte
3

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

Estendendo o Controlador de Navegação:

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

Desativando a rotação na única visualização

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

E girando para a orientação apropriada antes de seguir

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}
Misha
fonte
Você sabe se isso pode ajudar no meu problema, onde o controlador de exibição exibido fica em paisagem por um segundo antes de girar para o retrato como sempre deveria estar? A pergunta está aqui: stackoverflow.com/questions/30693964/…
dwinnbrown 11/11/2015
3

A melhor solução acima:

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

não funcionou para mim quando o chamei em viewDidAppear do controlador de exibição apresentado. No entanto, funcionou quando o chamei em preparForSegue no controlador de exibição de apresentação.

(Desculpe, não há pontos de reputação suficientes para comentar sobre essa solução, então eu tive que adicioná-la assim)

guido
fonte
Eu consegui isso funcionando, mas ao passar de um controlador de visualização em paisagem para um controlador de visualização em retrato, o controlador de exibição que deve aparecer em retrato é mostrado brevemente em paisagem antes de girar sozinho. se alguém sabe como ajudar a minha pergunta é aqui: stackoverflow.com/questions/30693964/...
dwinnbrown
@dwinnbrown at viewDidAppear o controlador de exibição já está visível. Talvez tente a partir de viewDidLoad?
Crashalot
@Crashalot Eu já corrigi isso. Por favor, veja a resposta que marquei como correta.
dwinnbrown
@dwinnbrown o link para sua pergunta está morto. atualizar? obrigado!
Crashalot
@Crashalot a comunidade excluiu, mas votei para anular a exclusão.
Avisarei
3

[iOS9 +] Se alguém arrastou todo o caminho até aqui, como nenhuma das soluções acima funcionou, e se você apresentar a visualização, deseja alterar a orientação seguindo, você pode verificar isso.

Verifique o tipo de apresentação do seu segue. O meu estava "acima do contexto atual". Quando mudei para Fullscreen, funcionou.

Graças a @GabLeRoux, encontrei esta solução.

  • Isso muda apenas quando combinado com as soluções acima.
Goh
fonte
2

Meus requisitos são

  1. bloquear todas as visualizações no modo retrato
  2. use AVPlayerViewControllerpara reproduzir vídeo

Quando o vídeo estiver 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 visualização apenas no modo retrato.

Primeiro, defina supportedInterfaceOrientationsForWindowemAppDelegate.swift

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

Segundo, no seu controlador de exibiçã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 direita, precisamos definir a rotação automática para ser verdadeira

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

Por fim, crie uma MyPlayerViewControllerinstância no seu 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 o seu player com o apropriado videoUrl, depois atribua-o a playerViewController. Feliz codificação!

charles.cc.hsu
fonte
2
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];
}
Roman Solodyashkin
fonte
1

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 UIViewControllerimplementação:

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

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

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

Neste exemplo, você também precisará definir as orientações permitidas do dispositivo para 'Paisagem à esquerda' nas configurações do seu projeto (ou diretamente info.plist). Apenas mude a orientação específica que você deseja forçar se quiser algo diferente deLandscapeLeft.

A chave para mim foi a attemptRotationToDeviceOrientationligação viewWillAppear- sem que a visualização não girasse corretamente sem girar fisicamente o dispositivo.

ViperMav
fonte
Método preterido.
Saqib Omer
1

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 a orientação específica

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

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

Criei o seguinte projeto objetivo-c github.com/GabLeRoux/RotationLockInTabbedViewChild com um exemplo de trabalho de TabbedViewControlleronde uma exibição filho é permitida girar e a outra exibição filho é bloqueada em retrato.

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

A exibição filho bloqueia a orientação dos pais

GabLeRoux
fonte
0

De acordo com a solução mostrada pelo @ sid-sha, você deve colocar tudo no viewDidAppear:método, caso contrário você não será didRotateFromInterfaceOrientation:acionado, então algo como:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
        interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
        NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
        [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    }
}
Loretoparisi
fonte
0

Minha solução

Em 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 no modo paisagem.

Em seguida, a solução do Sunny Shah funcionaria no seu XXViewControllerem qualquer versão do iOS:

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

Esta é a função de utilitário para encontrar o ViewController superior.

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
    }

}
duan
fonte
0

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

// Bloqueia a orientação para a paisagem direita

-(UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskLandscapeRight;
}

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

Eu tenho o mesmo problema e perco tanto tempo com isso. Então agora eu tenho minha solução. No entanto, algumas telas do meu aplicativo precisam ter apenas paisagem. Eu o corrigi com 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. Faça isso: antes de enviar / apresentar para o ViewControllerA atribuir isShouldRotate como true . Não se esqueça de quando pop / dispensar a atribuição do controlador isShouldRotate a false em viewWillDisappear .

lee
fonte
0

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

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

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

bduhbya
fonte
0

Parece que mesmo aqui, há tantas respostas que ninguém foi suficiente para mim. Eu queria forçar a orientação e depois voltar para a orientação do dispositivo, mas[UIViewController attemptRotationToDeviceOrientation]; simplesmente não funcionou. O que também complicou a coisa toda é que eu adicionei shouldAutorotate a false com base em alguma resposta e não consegui que os efeitos desejados retornassem corretamente em todos os cenários.

Então foi isso que eu fiz:

Antes de pressionar o controlador na chamada em seu construtor init, isso:

_userOrientation = UIDevice.currentDevice.orientation;
[UIDevice.currentDevice setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
[self addNotificationCenterObserver:@selector(rotated:)
                               name:UIDeviceOrientationDidChangeNotification];

Então, eu salvo a última orientação do dispositivo e me registro no evento de mudança de orientação. O evento de mudança de orientação é simples:

- (void)rotated:(NSNotification*)notification {
    _userOrientation = UIDevice.currentDevice.orientation;
}

E, na visão dissimulante, apenas forço a retornar a qualquer orientação que eu tenha como userOreintation:

- (void)onViewDismissing {
    super.onViewDismissing;
    [UIDevice.currentDevice setValue:@(_userOrientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
}

E isso tem que estar lá também:

- (BOOL)shouldAutorotate {
    return true;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}

E também o controlador de navegação deve delegar para shouldAutorotate e supportedInterfaceOrientations, mas acredito que a maioria das pessoas já possui.

PS: Desculpe, eu uso algumas extensões e classes base, mas os nomes são bastante significativos, então o conceito é compreensível, fará extensões ainda mais porque não é muito bonito agora.

Renetik
fonte