shouldAutorotateToInterfaceOrientation no se llama en iOS 6

Estoy usando MGSplitViewController y estoy usando shouldAutorotateToInterfaceOrientation para controlar el tamaño del controlador de vista maestra en rotación.

Todo está funcionando bien en iOS 5, pero en iOS 6 (tanto el simulador como el iPad) shouldAutorotateToInterfaceOrientation nunca se llama.

¿Es este un error que debería ser corregido con el lanzamiento final de iOS 6 o algo de lo que no tengo conocimiento ha cambiado?

POR FAVOR, LEA ESTO CUIDADOSAMENTE o puede perder 1 o 2 días de su vida con las nueces y luchando, sacudiendo, convirtiendo su dispositivo de prueba como el chimpancé en el zoológico que agarró el móvil de un visitante. Tarde o temprano … promesa 🙂

EN iOS 6

 shouldAutorotateToInterfaceOrientation: 

está en desuso y reemplazado por

 shouldAutorotate 

significa que iOS 6 nunca llamará a shouldAutorotateToInterfaceOrientation :

entonces si usaste lo siguiente en tu aplicación

ANTES de iOS6 (iOS5, iOS4 etc.)

 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { if (interfaceOrientation == UIInterfaceOrientationPortrait) { // your code for portrait mode } return YES; } 

Deberías usar

DESPUÉS de iOS 6+

 - (BOOL)shouldAutorotate { UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; if (orientation == UIInterfaceOrientationPortrait) { // your code for portrait mode } return YES; } 

TEN EN CUENTA

UIInterfaceOrientation es una propiedad de UIApplication y solo contiene 4 posibilidades que corresponden a la orientación de la barra de estado:

 UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft 

NO CONFUNDIRLO CON

UIDeviceOrientation que es una propiedad de la clase UIDevice y contiene 7 valores posibles:

 UIDeviceOrientationUnknown - Can not be determined UIDeviceOrientationPortrait - Home button facing down UIDeviceOrientationPortraitUpsideDown - Home button facing up UIDeviceOrientationLandscapeLeft - Home button facing right UIDeviceOrientationLandscapeRight - Home button facing left UIDeviceOrientationFaceUp - Device is flat, with screen facing up UIDeviceOrientationFaceDown - Device is flat, with screen facing down 

incluso teóricamente puedes usar la UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; que devuelve UIDeviceOrientation – la orientación real del dispositivo – PERO debe saber que UIDeviceOrientation no siempre es igual a UIInterfaceOrientation !!! Por ejemplo, cuando su dispositivo está en una tabla simple, puede recibir un valor inesperado.

Puede usar UIInterfaceOrientation orientation = self.interfaceOrientation; también que devuelve UIInterfaceOrientation , la orientación actual de la interfaz, PERO es una propiedad de UIViewController , por lo que puede acceder a esta solo en UIViewController clases de UIViewController .

Si desea admitir dispositivos anteriores iOS6 (iOS3 / 4/5) e iOS6, lo que podría ser evidente, solo use ambos shouldAutorotateToInterfaceOrientation: y shouldAutorotate en su código.

Desde iOS 6 hay 3 niveles y 3 pasos que el dispositivo verifica durante el inicio de la aplicación, que debe controlar si lo desea.

 1. Info.plist - Supported Interface Orientations 

que puedes establecer gráficamente en la pestaña Resumen. La secuencia de orientaciones permitidas es IMPORTANTE, que puede cambiar manualmente editando el info.plist , ¡y el dispositivo elegirá el primero cuando se info.plist la aplicación! Esto es crítico ya que el problema siempre es el inicio de la aplicación cuando existe la posibilidad de que la [UIDevice currentDevice].orientation sea ​​desconocida, especialmente cuando probamos nuestra aplicación en una superficie plana.

ajuste de plist en XCode (Info)

TENGA EN CUENTA Hay otras dos posibilidades de configuración con la extensión (iPad) o (iPhone), si usa alguna de ellas, usará esa configuración del dispositivo o simulador actual y descuidará la configuración general sin la extensión. Entonces, si ejecuta una aplicación solo para iPhone y accidentalmente dejó una línea de “Orientaciones de interfaz compatibles (iPad)” en algún lugar de la lista, incluso sin datos, descuidará las reglas que estableció anteriormente en la configuración general (en mi ejemplo para iPhone ) y podría recibir un rechazo por su aplicación con un mensaje de texto “Encontramos que su aplicación no cumplía con los requisitos para ejecutar en iPad …” incluso si su aplicación no tiene la intención de utilizar una orientación determinada en un iPhone, pero iPad lo usará, lo que podría causar errores imprevistos, ya que todas las aplicaciones de iPhone deben ejecutarse también en el iPad durante el proceso de envío.

 2. AppDelegate - application:supportedInterfaceOrientationsForWindow 

devolviendo una lista de máscara de bits de cada orientación que le gustaría permitir, que anula la configuración de info.plist. Se llama al menos una vez cada vez que el dispositivo gira.

 3. Top-level view controller or RootViewController - supportedInterfaceOrientations 

lo que da una intersección con el conjunto de la aplicación y el delegado de la aplicación, que debe tener un resultado distinto de cero para evitar el locking. Esto se llama al menos una vez cada vez que el dispositivo gira, excepto que hay otro método instalado en nuestro controlador:

 shouldAutorotate 

que interfiere con las orientaciones permitidas de la aplicación, y da un BOOL con el valor predeterminado YES .

 BE CAREFUL when you use `NavigationController` 

como el controlador superior en tu AppDelegate , así:

 DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil]; UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:detailViewController]; self.window.rootViewController =nil; self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; return YES; 

en este caso, debe colocar el siguiente código en su AppDelegate como una categoría adjunta a la clase NavigationController , ya que este es el controlador superior, y si no ha hecho una subcategoría de él, no tiene lugar / código donde puede establecer su configuración de orientación, por lo que debe forzarla a verificar su control de rootViewController real en este caso, el detailViewController para las orientaciones:

 @implementation UINavigationController (OrientationSettings_IOS6) -(BOOL)shouldAutorotate { return [[self.viewControllers lastObject] shouldAutorotate]; } -(NSUInteger)supportedInterfaceOrientations { return [[self.viewControllers lastObject] supportedInterfaceOrientations]; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation]; } @end 

después de esto, puede establecer las orientaciones preferidas en su ViewController “superior” (en mi ejemplo, este es el detailViewController ) con cualquiera de los métodos que tiene disponibles en iOS 6 para ViewControllers , como se muestra a continuación:

 1. (BOOL)shouldAutorotate 2. (NSUInteger)supportedInterfaceOrientations 3. (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation 

Ok, lo tengo para trabajar en iOS6 iPad Simulator. Hurra. Esto es lo que hice:

Solo te muestro antes y después, debería ser autoexplicativo:

ANTES DE

 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { if (interfaceOrientation==UIInterfaceOrientationPortrait) { // do some sh!t } return YES; } 

DESPUÉS

 - (BOOL)shouldAutorotate { UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation]; if (orientation==UIInterfaceOrientationPortrait) { // do some sh!t } return YES; } 

En cuanto a la orientación admitida, puede especificar en info.plist como tal: Foto de orientación admitida

O usa el código:

 -(NSUInteger)supportedInterfaceOrientations{ return UIInterfaceOrientationMaskPortrait; // etc } 

Editar: Pensándolo bien, si planea soportar versiones más bajas (iOS4.3 / 5 / 5.1) y 6.0, solo incluya ambos métodos con el mismo contenido de código. Funciona para mí (en sim de todos modos)

Configurar el controlador de ruta de la ventana de la aplicación en lugar de simplemente agregar su vista como una subvista que funcionó para mí (como lo hizo Rocotilos)

 // [self.window addSubview:self.topLevelNavigationController.view]; self.window.rootViewController = self.topLevelNavigationController; 

Aquí hay una solución para iOS 5 y un código anterior que no implica la duplicación de su lógica. Simplemente agregue este código a su controlador de vista y genera las OrientacionesInterfacer soportadas de su método existente shouldAutorotateToInterfaceOrientation.

 -(BOOL)shouldAutorotate{ return YES; } -(NSInteger)supportedInterfaceOrientations{ NSInteger mask = 0; if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeRight]) mask |= UIInterfaceOrientationMaskLandscapeRight; if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeLeft]) mask |= UIInterfaceOrientationMaskLandscapeLeft; if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationPortrait]) mask |= UIInterfaceOrientationMaskPortrait; if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationPortraitUpsideDown]) mask |= UIInterfaceOrientationMaskPortraitUpsideDown; return mask; } 

Una solución rápida para mí fue agregar este código en mi controlador de vista raíz

 - (BOOL)shouldAutorotate { return [self shouldAutorotateToInterfaceOrientation:self.interfaceOrientation]; } 

De esta forma, sigo usando la misma lógica que usé para las versiones anteriores a iOS 6. Por supuesto, originalmente había configurado mi control raíz en mi aplicación para delegates didFinishLaunchingWithOptions correctamente en lugar de solo agregar a las subvistas de Windows.

Y aunque algunas de las respuestas tienen la solución indicada, que es implementar shouldAutorotate y supportedInterfaceOrientations para iOS 6 comstackciones, también debe tener en cuenta que si su controlador de vista está alojado en un controlador de navegación, nada importará ya que el runtime llamará a estos en la instancia de UINavigationController e ignorará tu código de lo contrario.

Aparentemente, la ‘solución’ es subclase UINavigationController e implementar estos nuevos métodos en esta subclase como se describe aquí: iOS 6 UITabBarController orientación compatible con el controlador UINavigation actual

Y luego tiene el placer de cambiar todo su código donde usa un UINavigationController para usar esta nueva subclase.

Este tiene que ser uno de los cambios de ruptura más inútiles y molestos en una versión de iOS que he visto.

IOS 5

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

ios 6

 -(BOOL)shouldAutorotate{ return YES; } -(NSInteger)supportedInterfaceOrientations:(UIWindow *)window{ // UIInterfaceOrientationMaskLandscape; // 24 // // UIInterfaceOrientationMaskLandscapeLeft; // 16 // // UIInterfaceOrientationMaskLandscapeRight; // 8 // // UIInterfaceOrientationMaskPortrait; // 2 // return UIInterfaceOrientationMaskLandscape; return 24; } 

Esto funcionó para mí con iOS6 y Xcode 4.5 GM:

En AppDelegate.m

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ..... // self.window.rootViewController = self.navigationController; [window setRootViewController:navigationController]; ..... return YES; } 

en ViewController:

 - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) { // landscape } else { //portrait } } 

Aquí hay una cita del iOS SDK de Apple, XCode4.5 + (ver Referencia de clase UIViewController, Manejo de rotaciones de vista):

En iOS 6, su aplicación admite las orientaciones de interfaz definidas en el archivo Info.plist de su aplicación. Un controlador de vista puede anular el método supportedInterfaceOrientations para limitar la lista de orientaciones admitidas. En general, el sistema llama a este método solo en el controlador de vista raíz de la ventana o un controlador de vista presentado para llenar toda la pantalla ; los controladores de vista secundarios usan la porción de la ventana proporcionada por su controlador de vista principal y ya no participan directamente en las decisiones sobre qué rotaciones son compatibles.

También en iOS6, el método shouldAutorotateToInterfaceOrientation: de la clase UIViewController ya está en desuso.

Entonces en tu controlador de vista raíz, haces el ff:

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

Por cierto, “controlador de vista raíz” es cualquier subclase UIViewController que establezca como el control de raíz del objeto de la ventana de su aplicación . Usualmente haces esto en la aplicación appDelegate : didFinishLaunchingWithOptions: method.

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.rootViewController = [FFDashboardController create]; [self.window makeKeyAndVisible]; return YES; } 

En cuanto a usar UINavigationController como VC raíz, verifique la respuesta de Vladimir .

En respuesta a @Rocotilos, tengo un apéndice que ocurrió en mi código que no he visto en otros foros. Me encontré con una situación en la que al principio del ciclo de vida de la aplicación, donde la orientación no se conocía en el método shouldAutorotate. Esto provocaba que la aplicación no mostrara la vista correctamente en el paisaje. Después de algunas rotaciones en el simulador, funcionaba bien.

Mi hipótesis es que hay ciertas vistas que se muestran en donde nos gustaría el diseño del paisaje. Como tal, no queremos que shouldAutorotate regrese SÍ. Me doy cuenta de que este podría ser un caso raro para algunos, pero he pasado mucho tiempo diagnosticando esto y quería dejarlo pasar. Espero que esto sea útil.

 - (BOOL) shouldAutorotate { BOOL shouldRotate = NO; UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation]; if ([self IsCaseWhereWeDontWantLandscapeAutorotation]) { shouldRotate = NO; } else if (orientation == UIDeviceOrientationUnknown) { //Handle the case where the device's orientation is not yet known shouldRotate = YES; } else { //Handle the normal case shouldRotate = (orientation == UIInterfaceOrientationMaskLandscape); } return shouldRotate; } 

Resulta que solo la vista raíz maneja estas llamadas. En mi caso, este fue un UINavigationController normal. Tuve que cambiar esto a un archivo subclasificado donde agregué los métodos.

En mi caso, solo quería el retrato de la vista de la raíz, y el rest retrato + paisaje.

Appdelegate.h

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. S2MBookAppRootViewController *masterViewController = [[[S2MBookAppRootViewController alloc] initWithNibName:pref.rootNibName bundle:nil] autorelease]; self.navigationController = [[[S2MBookAppNavController alloc] initWithRootViewController:masterViewController] autorelease]; self.window.rootViewController = self.navigationController; [self.window makeKeyAndVisible]; [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque animated:YES]; return YES; } 

Y el S2MBookAppNavController (subclase de UINavigationController)

 - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { if([self.viewControllers count] == 1) return UIInterfaceOrientationMaskPortrait; return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape; } 

ACTUALIZACIÓN: cuando quiera forzar la orientación (al presionar una nueva vista en el controlador de navegación) intente esto.

Haga que su UINavigationController también sea su propio delegado:

 @interface S2MBookAppNavController : UINavigationController  @end 

Y en el archivo .m

 - (void)viewDidLoad { [super viewDidLoad]; self.delegate = self; // Do any additional setup after loading the view. } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([UIViewController respondsToSelector:@selector(attemptRotationToDeviceOrientation)]) { //present/dismiss viewcontroller in order to activate rotating. UIViewController *mVC = [[[UIViewController alloc] init] autorelease]; [self presentModalViewController:mVC animated:NO]; [self dismissModalViewControllerAnimated:NO]; } } 

Sí, el problema es que solo se llama al método window.rootViewController para devolver la máscara de orientación. El método supportedInterfaceOrientations debe implementarse en viewController, que está configurado como window.rootViewController. Pero en la mayoría de los casos este objeto no es de clase personalizada, por ejemplo, UINavigationController. Una posible solución es subclasificar UINavigationController. Pero Apple dice: “Esta clase no está destinada a subclases”, así que preferiría usar otro UIViewController para manejar las orientaciones y agregar UINavigationController como elemento secundario.

 MyRootViewController * myRoot = [MyRootViewController new]; self.window.rootViewController = myRoot; [myRoot addChildViewController:navigationController]; [myRoot.view addSubview:navigationController.view]; 

y en MyRootViewController implementar métodos:

 - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { // return your mask here eg: return UIInterfaceOrientationMaskPortrait; } 

Tengo una serie de controladores de visualización en un UINavigationController, uno de los cuales debe ser solo horizontal. Lo arreglé subclasificando el UINavigationController y agregando el siguiente código:

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

Esto asegura que el controlador de vista superior dicta la orientación.

Sin embargo, hay un problema: si te estás moviendo de una vc que está limitada a un paisaje a una que admite cualquier orientación, la nueva vista se mostrará en horizontal independientemente de la orientación del teléfono. Para combatir esto, puse el siguiente código en viewControllers ilimitados:

 - (NSUInteger)supportedInterfaceOrientations{ if(UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) return UIInterfaceOrientationMaskPortrait; else return UIInterfaceOrientationMaskLandscape; } 
 -(BOOL)shouldAutorotate { UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; if (orientation == UIDeviceOrientationUnknown) { return YES; } return [self shouldAutorotateToInterfaceOrientation:self.interfaceOrientation]; } 

Las notas bajo el encabezado UIKit aquí: http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html%23//apple_ref/doc/uid/TP40012166-CH1- SW19 da algunas pistas sobre la respuesta, pero no es la imagen completa. Todavía no tengo toda la imagen, pero esto es lo que he encontrado hasta ahora para iOS 6.0 RTM.

Si realmente no está restringiendo las orientaciones admitidas y, en cambio, quiere hacer algo porque el usuario rotó el dispositivo, puede mover la lógica en

 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 

a

 - (void)viewWillLayoutSubviews 

en lugar.

Esto probablemente sea un reemplazo directo, pero todavía no lo he probado en versiones de iOS de nivel inferior.

Si desea restringir las orientaciones admitidas, debe hacerlo en el nivel UIApplicationDelegate con

 -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 

La documentación establece que “el sistema determina si una orientación es compatible al intersectar el valor devuelto por el método supportedInterfaceOrientationsForWindow: con el valor devuelto por el método supportedInterfaceOrientations del controlador de pantalla completa superior”. pero en la experimentación he encontrado que el sistema ignora mis controladores de vista compatibles

 -(NSUInteger)supportedInterfaceOrientations 

valor de retorno, aunque se llame al método.

Apple ha dejado de utilizar el método owdautorate de ios6 en su lugar, use estos métodos. Mira tus documentos xcode

 - (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0); - (NSUInteger)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0); // Returns interface orientation masks. - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0); 

Está trabajando para mí:

 - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { NSUInteger orientations = UIInterfaceOrientationMaskPortrait; if ([self isKindOfClass:[YourViewController class]]) { // Your view class orientations |= UIInterfaceOrientationMaskPortraitUpsideDown; } return orientations; } 

orientaciones:

 orientations |= UIInterfaceOrientationMaskLandscapeLeft; orientations |= UIInterfaceOrientationMaskLandscapeRight;