Cómo forzar un UIViewController a la orientación vertical en iOS 6

85

Como ShouldAutorotateToInterfaceOrientationestá obsoleto en iOS 6 y lo usé para forzar una vista particular a solo vertical , ¿cuál es la forma correcta de hacer esto en iOS 6? Esto es solo para un área de mi aplicación, todas las demás vistas pueden rotar.

Neal
fuente

Respuestas:

117

Si desea que todos nuestros controladores de navegación respeten el controlador de vista superior, puede usar una categoría para no tener que pasar y cambiar un montón de nombres de clases.

@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 señalan algunos de los comentarios, esta es una solución rápida al problema. Una mejor solución es la subclase UINavigationController y poner estos métodos allí. Una subclase también ayuda a soportar 6 y 7.

Antonio
fuente
3
Puedo confirmar que esto funciona. También puede reemplazar "[self.viewControllers lastObject]" por "self.topViewController" si lo desea.
Wayne Liu
3
Si está utilizando UITabBarController, esta categoría UINavigationController no es de ayuda. En su lugar, debería crear una categoría en UITabBarController ...
Borut Tomazin
46
El problema con esta solución es que no funciona cuando el controlador emergente que estaba en horizontal y su nuevo controlador superior solo admite retrato (o viceversa), esas devoluciones de llamada no se llaman en este caso y todavía tengo que encontrar la manera de hacerlo. fuerce el nuevo controlador superior en la orientación correcta. ¿Algunas ideas?
Lope
4
Subclasé UINavigationController y lo configuré como window.rootController. Implementé los 3 métodos, funciona muy bien cuando cambia la rotación del dispositivo, el problema es que esos métodos (y nada relacionado con la rotación) se llama cuando abre el controlador de vista
Lope
4
Si estoy haciendo push o pop desde el controlador de vista horizontal, esta fuerza UIViewController cambia a Landscape. Sin embargo, si giro a retrato, funciona bien y nunca cambiará a paisaje. El único problema al empujar o salir del paisaje. Por favor ayuda
Tariq
63

La mejor forma para iOS6 se indica específicamente en "iOS6 por tutoriales" del equipo de Ray Wenderlich - http://www.raywenderlich.com/ y es mejor que la subclasificación UINavigationControlleren la mayoría de los casos.

Estoy usando iOS6 con un guión gráfico que incluye un UINavigationControllerconjunto como controlador de vista inicial.

//AppDelegate.m: desafortunadamente, este método no está disponible antes de iOS6

- (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: devuelve las orientaciones que quieras admitir para cada UIViewController

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}
Phil
fuente
1
Esta es la mejor solución para iOS 6
Homam
2
@Phil, funciona bien para iOS 6. Pero en algunos casos, como si pasa de un paisaje a otro, entonces no funciona. ¿Alguna idea de por qué sucede?
Kirti Nikam
1
Solución muy útil. Para mi lo mejor.
oscar castellon
1
Trabajando en iOS 7 beta 6. Funciona al pasar de paisaje a retrato en mi caso.
Martin Berger
Estoy en iOS 6 pero esto no funciona. Se rompe en la línea "UIViewController * presentsViewController".
Marcel Marino
39

Esta respuesta se relaciona con las preguntas formuladas en los comentarios de la publicación del OP:

Para forzar que una vista aparezca en una orientación determinada, ponga lo siguiente en viewWillAppear:

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

Es un truco, pero esto obliga UIViewControllera que se presente en vertical incluso si el controlador anterior era horizontal.

ACTUALIZAR para iOS7

Los métodos anteriores ahora están en desuso, por lo que para iOS 7 use lo siguiente:

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, en el momento de la escritura, ya sea presente o despedir debe estar animada. Si ninguno de los dos lo está, obtendrá una pantalla en blanco. No tengo idea de por qué esto lo hace funcionar, ¡pero lo hace! El efecto visual es diferente según el que esté animado.

Craig Watkinson
fuente
Gracias, lo intentaré cuando encuentre unos minutos de tiempo libre
Lope
@RohanAgarwal funciona como se anuncia, lo estoy usando aquí. Pero también debe tener implementado el código de respuesta aceptado, o no funcionará.
Dennis Munsie
2
¿Alguien ha descubierto cómo hacer que esto funcione con la animación de rotación correcta al mismo tiempo? Es un poco discordante verlo cambiar de retrato a paisaje sin la animación. Funciona, solo con la esperanza de que funcione un poco mejor.
Dennis Munsie
@Craig Watkinson No está funcionando en el guión gráfico. y presentModalViewController y desecharModalViewControllerAnimated está en desuso si iam usa presentVirecontroller, entonces no funcionará
favor
1
Para ios8, desecharViewControllerAnimated simplemente se bloquea. Encontré que llamando a [NWSAppDelegate sharedInstance] .window.rootViewController = nil; [NWSAppDelegate sharedInstance] .window.rootViewController = previousRootController funciona bien.
Alexey
36

Así que me encontré con el mismo problema al mostrar vistas modales solo de retrato. Normalmente, crearía un UINavigationController, establecería el viewControllercomo el rootViewControllery luego lo mostraría UINavigationControllercomo una vista modal. Pero con iOS 6, elviewController ahora le pedirá al navigationController las orientaciones de interfaz compatibles (que, de forma predeterminada, ahora es todo para iPad y todo menos al revés para iPhone).

Solución : tuve que crear una subclase UINavigationControllery anular los métodos de autorrotación. Algo cojo.

- (BOOL)shouldAutorotate {
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}
// pre-iOS 6 support 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
rocoso
fuente
1
no funciona en el mío. Tenía este código con global configurado para todas las orientaciones
phil88530
2
supportedInterfaceOrientations debe devolver un NSUInteger, no un BOOL. Ver developer.apple.com/library/ios/#featuredarticles/…
tomwhipple
funcionó, para mí es el tabbarcontroller, nuevamente gracias por la respuesta
otakuProgrammer
FWIW, tuve que ir por esta ruta también ... Nada más funcionó. Gracias.
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
fuente
¡Excelente! Necesita estar al máximo en la parte superior de este hilo. Porque es la forma más sencilla de resolver problemas
Alex
@Roshan Jalgaonkar En ios 6 No funciona para mí, solo necesito un retrato con el botón de inicio hacia abajo, ¿cómo puedo configurar esta orientación UIO ...?
Karthik
@Karthik, debes habilitar las rotaciones admitidas en el objetivo de tu aplicación para que esto funcione.
Alejandro Iván
@ AlejandroIván k thnx
Karthik
10

No estoy de acuerdo con la respuesta de @aprato, porque los métodos de rotación de UIViewController se declaran en las categorías mismas, lo que da como resultado un comportamiento indefinido si anula luego en otra categoría. Es más seguro anularlos en una subclase UINavigationController (o UITabBarController)

Además, esto no cubre el escenario en el que empuja / presenta / emerge desde una vista horizontal a un VC solo vertical o viceversa. Para resolver este difícil problema (nunca abordado por Apple), debe:

En iOS <= 4 y iOS> = 6:

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

En iOS 5:

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

Estos REALMENTE obligarán a UIKit a reevaluar todas sus orientaciones shouldAutorotate, supportedInterfaceOrientations, etc.

Rafael Nobre
fuente
2
El primero de estos se bloquea independientemente de la versión de iOS. Dice que no se debe llamar a despedir antes de que haya terminado el presente.
arsenio
@arsenius ¿Puedes publicar un fragmento de cómo lo estás usando? Mientras no esté animado, no debería tener el problema que mencionas
Rafael Nobre
También puse el segundo fragmento en viewDidAppear y no hace nada para ayudar a mi situación en iOS 6. La pregunta está aquí: stackoverflow.com/questions/15654339/…
arsenius
Lo siento, un último comentario aquí. Mover el código de iOS 5 a viewDidAppear crea una especie de bucle infinito con esta salida: - Desbordamiento de [UIApplication beginIgnoringInteractionEvents]. Postergación.
arsenius
3

Tengo un muy buen enfoque mezclando https://stackoverflow.com/a/13982508/2516436 y 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;
    }
}

y devuelva las orientaciones que desee apoyar para cada UIViewController

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

Tengo una aplicación universal relativamente compleja que usa UISplitViewController y UISegmentedController, y tengo algunas vistas que deben presentarse en Landscape usando presentViewController. Usando los métodos sugeridos anteriormente, pude hacer que el iPhone ios 5 y 6 funcionaran de manera aceptable, pero por alguna razón el iPad simplemente se negó a presentarse como Paisaje. Finalmente, encontré una solución simple (implementada después de horas de lectura y prueba y error) que funciona tanto para dispositivos como para ios 5 y 6.

Paso 1) En el controlador, especifique la orientación requerida (más o menos como se indica arriba)

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

-(BOOL)shouldAutorotate
{
    return YES;
}

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

}

Paso 2) Cree una subclase UINavigationController simple e implemente los siguientes métodos

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

Paso 3) Presenta tu viewController

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

Espero que esto sea útil para alguien.

usuario216661
fuente
1

No quiero ser aburrido aquí, pero ¿sería tan amable de compartir su subclase? Gracias.

editar: bueno, finalmente lo hice, la subclase era muy simple de hacer. Solo tuve que declarar el navigationControlleren AppDelegatecomo en UINavigationControllerSubclasslugar del predeterminado UINavigationController, luego modifiqué su subclase con:

- (BOOL)shouldAutorotate {
    return _shouldRotate;
}

para que pueda configurar cualquier vista que quiera rotar o no llamando al viewDidLoad

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

Espero que este ajuste ayude a otros también, ¡gracias por tu consejo!

Consejo: utilice

- (NSUInteger)supportedInterfaceOrientations

en sus controladores de vista, para que no termine teniendo una vista vertical deseada en horizontal o viceversa.

Roland
fuente
0

No lo probé yo mismo, pero la documentación indica que ahora puede anular esos métodos: supportedInterfaceOrientationsypreferredInterfaceOrientationForPresentation .

Probablemente pueda lograr lo que desea y establecer solo la orientación que desee en esos métodos.

J_D
fuente
0

Las respuestas que usan subclases o categorías para permitir VC dentro de las clases UINavigationController y UITabBarController funcionan bien. Error al iniciar un modo de solo retrato desde un controlador de barra de pestañas horizontal. Si necesita hacer esto, utilice el truco de mostrar y ocultar una vista modal no animada, pero hágalo en el método viewDidAppear . No funcionó para mí en viewDidLoad o viewWillAppear.

Aparte de eso, las soluciones anteriores funcionan bien.

Pedro
fuente
0

Para Monotouch, puede hacerlo de esta manera:

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

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

Simplemente vaya a project.plist, luego agregue la orientación de la interfaz compatible y luego agregue solo Vertical (botón de inicio inferior) y Vertical (botón de inicio superior).

Puede agregar o eliminar la orientación según los requisitos de su proyecto.

Gracias

Muhammad Saad Ansari
fuente
1
se trata de un controlador de vista particular, no para toda la aplicación.
Muhammad Rizwan
-2

1) Verifique la configuración de su proyecto e info.plist y asegúrese de que solo estén seleccionadas las orientaciones que desea.

2) agregue los siguientes métodos a su controlador de vista superior (controlador de navegación / controlador de barra de pestañas)

- (NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;

}

3) agregue los siguientes métodos al delegado de su aplicación

- (NSUInteger) supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;

}

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

}
ehanna
fuente
-3

Ponga esto en el archivo .m de cada ViewControlleruno que no quiera rotar:

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

Consulte aquí para obtener más información.

Artillero de la Marina
fuente