Como forçar um UIViewController para a orientação Retrato no iOS 6

85

Como o ShouldAutorotateToInterfaceOrientationestá obsoleto no iOS 6 e usei-o para forçar uma determinada visualização para apenas retrato , qual é a maneira correta de fazer isso no iOS 6? Isso é apenas para uma área do meu aplicativo, todas as outras visualizações podem girar.

Neal
fonte

Respostas:

117

Se você quiser que todos os nossos controladores de navegação respeitem o controlador de vista superior, você pode usar uma categoria para que não precise alterar vários nomes de classe.

@implementation UINavigationController (Rotation_IOS6)

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

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

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

@end

Como alguns comentários apontam, esta é uma solução rápida para o problema. Uma solução melhor é a subclasse UINavigationController e colocar esses métodos lá. Uma subclasse também ajuda a suportar 6 e 7.

Anthony
fonte
3
Posso confirmar que isso funciona. Você também pode substituir "[self.viewControllers lastObject]" por "self.topViewController" se desejar.
Wayne Liu
3
Se você estiver usando UITabBarController, essa categoria UINavigationController não ajudará em nada. Em vez disso, você deve criar uma categoria no UITabBarController ...
Borut Tomazin
46
O problema com esta solução é que não funciona quando você ativa o controlador que estava no modo paisagem e seu novo controlador superior suporta apenas retrato (ou vice-versa), esses retornos de chamada não são chamados neste caso e ainda estou para descobrir como forçar o novo controlador superior na orientação correta. Alguma ideia?
Lope
4
Eu criei uma subclasse de UINavigationController e defini-o como window.rootController. Implementei todos os 3 métodos, funciona muito bem quando você altera a rotação do dispositivo, o problema é que esses métodos (e nada relacionado à rotação) são chamados quando você abre o controlador de visualização
Lope
4
Se estou fazendo push ou pop a partir do controlador de visualização de paisagem, isso força UIViewController muda para Paisagem. No entanto, se eu girar para retrato, ele funciona bem e nunca mudará para paisagem. O único problema ao empurrar ou sair da paisagem. Por favor, ajude
Tariq
63

A melhor maneira para iOS6 especificamente é observada em "iOS6 por Tutoriais" pela equipe de Ray Wenderlich - http://www.raywenderlich.com/ e é melhor do que criar subclasses UINavigationControllerna maioria dos casos.

Estou usando o iOS6 com um storyboard que inclui um UINavigationControllerconjunto como controlador de visualização inicial.

//AppDelegate.m - este método não está disponível pré-iOS6, infelizmente

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;

if(self.window.rootViewController){
    UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
    orientations = [presentedViewController supportedInterfaceOrientations];
}

return orientations;
}

//MyViewController.m - retorna as orientações que deseja suportar para cada UIViewController

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}
Phil
fonte
1
Esta é a melhor solução para iOS 6
Homam
2
@Phil, funciona bem para iOS 6. Mas em alguns casos, como se você passar de paisagem para retrato, então não funciona. Alguma ideia de por que isso acontece?
Kirti Nikam
1
Solução muito útil. Para mim, o melhor.
oscar castellon
1
Trabalhando no iOS 7 beta 6. Funciona ao passar de paisagem para retrato no meu caso.
Martin Berger,
Estou no iOS 6, mas não funciona. Ele quebra na linha "UIViewController * associatedViewController".
Marcel Marino
39

Esta resposta está relacionada às perguntas feitas nos comentários da postagem do OP:

Para forçar uma visualização a aparecer em uma determinada orientação, coloque o seguinte em viewWillAppear:

UIApplication* application = [UIApplication sharedApplication];
if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
{
    UIViewController *c = [[UIViewController alloc]init];
    [self presentModalViewController:c animated:NO];
    [self dismissModalViewControllerAnimated:NO];
}

É um pouco hack, mas isso força o UIViewControllera ser apresentado em retrato, mesmo se o controlador anterior fosse paisagem

ATUALIZAÇÃO para iOS7

Os métodos acima agora estão obsoletos, portanto, para iOS 7, use o seguinte:

UIApplication* application = [UIApplication sharedApplication];
if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
{
     UIViewController *c = [[UIViewController alloc]init];
     [c.view setBackgroundColor:[UIColor redColor]];
     [self.navigationController presentViewController:c animated:NO completion:^{
            [self.navigationController dismissViewControllerAnimated:YES completion:^{
            }];
     }];
}

Curiosamente, no momento da escrita, quer no presente ou descartar deve ser animada. Caso contrário, você receberá uma tela branca. Não tenho ideia de por que isso faz funcionar, mas funciona! O efeito visual é diferente dependendo de qual é animado.

Craig Watkinson
fonte
Obrigado, vou tentar quando tiver alguns minutos de tempo livre
Lope
@RohanAgarwal funciona como anunciado, estou usando aqui. Mas você também precisa ter o código da resposta aceita implementado, ou não funcionará.
Dennis Munsie
2
Alguém descobriu como fazer isso funcionar com a animação de rotação correta ao mesmo tempo? É um pouco chocante vê-lo mudar de retrato para paisagem sem a animação. Funciona, apenas esperando que funcione um pouco melhor.
Dennis Munsie
@Craig Watkinson Não está funcionando no storyboard. e presentModalViewController e CC0ModalViewControllerAnimated foi descontinuado se eu usar presentVirecontroller, então não funcionará. por favor me ajude
Kalpesh
1
Para ios8 despedirViewControllerAnimated apenas trava. Descobri que chamar [NWSAppDelegate sharedInstance] .window.rootViewController = nil; [NWSAppDelegate sharedInstance] .window.rootViewController = previousRootController funciona bem.
Alexey
36

Então, encontrei o mesmo problema ao exibir apenas visualizações modais de retrato. Normalmente, eu criaria um UINavigationController, definiria viewControllercomo e rootViewController, em seguida, exibiria o UINavigationControllercomo uma visualização modal. Mas com o iOS 6, oviewController irá agora pedir ao navigationController as orientações de interface suportadas (que, por padrão, agora é tudo para iPad e tudo menos de cabeça para baixo para iPhone).

Solução : Tive que criar uma subclasse UINavigationControllere substituir os métodos de auto-rotação. Meio coxo.

- (BOOL)shouldAutorotate {
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}
// pre-iOS 6 support 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
rochoso
fonte
1
não funciona no meu. Eu tinha esse código com global definido para todas as orientações
phil88530
2
supportInterfaceOrientations deve retornar um NSUInteger, não BOOL. Consulte developer.apple.com/library/ios/#featuredarticles/…
tomwhipple,
funcionou, para mim é o tabbarcontroller, mais uma vez obrigado pela resposta
otakuProgrammer
FWIW, eu tive que ir por esse caminho também ... Nada mais funcionou. Obrigado.
Steve N
15

IOS 5

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{

    return (interfaceOrientation == UIInterfaceOrientationPortrait);

}

IOS 6

-(BOOL)shouldAutorotate{
    return YES;
}

-(NSInteger)supportedInterfaceOrientations{

    //    UIInterfaceOrientationMaskLandscape;
    //    24
    //
    //    UIInterfaceOrientationMaskLandscapeLeft;
    //    16
    //
    //    UIInterfaceOrientationMaskLandscapeRight;
    //    8
    //
    //    UIInterfaceOrientationMaskPortrait;
    //    2

    //    return UIInterfaceOrientationMaskPortrait; 
    //    or
          return 2;
}
Roshan Jalgaonkar
fonte
Excelente! Precisa estar ao máximo no topo deste tópico. Porque é a maneira mais simples de resolver o problema
Alex
@Roshan Jalgaonkar In ios 6 Não funciona para mim, eu preciso apenas de retrato com o botão home para baixo como posso definir esta orientação UIO ....
Karthik
@Karthik, você deve habilitar as rotações com suporte no destino do seu aplicativo para que isso funcione.
Alejandro Iván
10

Eu discordo da resposta @aprato, porque os métodos de rotação UIViewController são declarados nas próprias categorias, resultando em um comportamento indefinido se você substituí-los em outra categoria. É mais seguro substituí-los em uma subclasse UINavigationController (ou UITabBarController)

Além disso, isso não cobre o cenário onde você empurra / apresenta / pop de uma visualização Paisagem para um retrato apenas VC ou vice-versa. Para resolver esse problema difícil (nunca abordado pela Apple), você deve:

No iOS <= 4 e iOS> = 6:

UIViewController *vc = [[UIViewController alloc]init];
[self presentModalViewController:vc animated:NO];
[self dismissModalViewControllerAnimated:NO];
[vc release];

No iOS 5:

UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

Isso REALMENTE forçará o UIKit a reavaliar todas as suas shouldAutorotate, supportedInterfaceOrientations, etc.

Rafael Nobre
fonte
2
A primeira dessas falhas para mim, independentemente da versão do iOS. Diz que despedir não deve ser chamado antes que o presente termine.
Arsenius
@arsenius Você pode postar um trecho de como o está usando? Enquanto não for animado não deveria ter o fascículo que você mencionou
Rafael Nobre
Eu também coloquei o segundo snippet em viewDidAppear e ele não ajuda em nada minha situação no iOS 6. A pergunta está aqui: stackoverflow.com/questions/15654339/…
arsenius
Desculpe, um último comentário aqui. Mover o código do iOS 5 para viewDidAppear cria algum tipo de loop infinito com esta saída: - [UIApplication beginIgnoringInteractionEvents] estouro. Ignorando.
arsenius
3

Tenho uma abordagem muito boa misturando https://stackoverflow.com/a/13982508/2516436 e https://stackoverflow.com/a/17578272/2516436

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
    NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;


    if(self.window.rootViewController){
        UIViewController *presentedViewController = [self topViewControllerWithRootViewController:self.window.rootViewController];
        orientations = [presentedViewController supportedInterfaceOrientations];
    }

    return orientations;
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}

e retornar quaisquer orientações que você deseja oferecer suporte para cada UIViewController

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}
Alex Guerra
fonte
1

Eu tenho um aplicativo universal relativamente complexo usando UISplitViewController e UISegmentedController, e tenho algumas visualizações que devem ser apresentadas em Paisagem usando presentViewController . Usando os métodos sugeridos acima, consegui fazer com que o iPhone ios 5 e 6 funcionasse de forma aceitável, mas por algum motivo o iPad simplesmente se recusou a apresentar como paisagem. Por fim, encontrei uma solução simples (implementada após horas de leitura e tentativa e erro) que funciona para dispositivos e iOS 5 e 6.

Etapa 1) No controlador, especifique a orientação necessária (mais ou menos conforme observado acima)

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

-(BOOL)shouldAutorotate
{
    return YES;
}

-(NSUInteger)supportedInterfaceOrientations
{
    NSInteger mask = UIInterfaceOrientationMaskLandscape;
    return mask;

}

Etapa 2) Crie uma subclasse UINavigationController simples e implemente os seguintes métodos

-(BOOL)shouldAutorotate {
        return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
        return UIInterfaceOrientationMaskLandscape;
}

Etapa 3) Apresente seu viewController

vc = [[MyViewController alloc]init];
MyLandscapeNavigationController *myNavigationController = [[MyLandscapeNavigationController alloc] initWithRootViewController:vc];
[self myNavigationController animated:YES completion:nil];

Espero que isto seja útil para alguém.

user216661
fonte
1

Não quero ser chato aqui, mas você faria a gentileza de compartilhar sua subclasse? Obrigado.

editar: bem, eu finalmente consegui, a subclasse era muito simples de fazer. Eu apenas tive que declarar o navigationControllerin the as AppDelegateem UINavigationControllerSubclassvez do default UINavigationController, então modifiquei sua subclasse com:

- (BOOL)shouldAutorotate {
    return _shouldRotate;
}

então posso definir qualquer visão que eu queira girar ou não ligando para viewDidLoad

_navController = (UINavigationController *)self.navigationController;
[_navController setShouldRotate : YES / NO]

Espero que este ajuste ajude outras pessoas também, obrigado pela sua dica!

Dica: Faça uso de

- (NSUInteger)supportedInterfaceOrientations

em seus controladores de visualização, para que você não termine tendo uma visualização desejada de retrato em paisagem ou vice-versa.

Roland
fonte
0

Eu não testei, mas a documentação afirma que agora você pode substituir esses métodos: supportedInterfaceOrientationse preferredInterfaceOrientationForPresentation.

Você provavelmente pode conseguir o que deseja definindo apenas a orientação que deseja nesses métodos.

J_D
fonte
0

As respostas usando subclasses ou categorias para permitir VCs dentro das classes UINavigationController e UITabBarController funcionam bem. Falha ao iniciar um modal somente retrato de um controlador de barra de guias em paisagem. Se você precisar fazer isso, use o truque de exibir e ocultar uma visualização modal não animada, mas faça isso no método viewDidAppear . Não funcionou para mim em viewDidLoad ou viewWillAppear.

Além disso, as soluções acima funcionam bem.

Peter
fonte
0

Para Monotouch, você pode fazer desta forma:

public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
    {
    return UIInterfaceOrientationMask.LandscapeRight;
}

public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation()
    {
    return UIInterfaceOrientation.LandscapeRight;
}
TJS
fonte
-1

Basta ir para project.plist e adicionar Orientação da interface compatível e adicionar apenas Retrato (botão home inferior) e Retrato (botão home superior).

Você pode adicionar ou remover a orientação de acordo com os requisitos do projeto.

obrigado

Muhammad Saad Ansari
fonte
1
trata-se de um controlador de visão particular, não para toda a aplicação.
Muhammad Rizwan de
-2

1) Verifique as configurações do projeto e info.plist e certifique-se de que apenas as orientações que você deseja estão selecionadas.

2) adicione os seguintes métodos ao seu controlador de visualização superior (controlador de navegação / controlador de barra de guias)

- (NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;

}

3) adicione os seguintes métodos ao delegado do seu aplicativo

- (NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;

}

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    return UIInterfaceOrientationMaskPortrait;

}
ehanna
fonte
-3

Coloque isso no arquivo .m de cada um que ViewControllervocê não deseja girar:

- (NSUInteger)supportedInterfaceOrientations
{
    //return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
    return UIInterfaceOrientationMaskPortrait;
}

Veja aqui para mais informações.

Artilheiro da Marinha
fonte