Eu tenho um aplicativo onde eu gostaria de apoiar a rotação do dispositivo em certas vistas, mas outras não fazem sentido no modo Paisagem, então, ao trocar as vistas, gostaria de forçar a rotação a ser definida para retrato.
Há um setter de propriedade não documentado no UIDevice que faz o truque, mas obviamente gera um aviso do compilador e pode desaparecer com uma revisão futura do SDK.
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
Existem maneiras documentadas de forçar a orientação?
Atualização: Eu pensei em fornecer um exemplo, já que não estou procurando shouldAutorotateToInterfaceOrientation, pois já implementei isso.
Eu quero que meu aplicativo suporte paisagem e retrato na Visualização 1, mas apenas retrato na Visualização 2. Eu já implementei shouldAutorotateToInterfaceOrientation para todas as visualizações, mas se o usuário estiver no modo paisagem na Visualização 1 e alternar para a Visualização 2, eu quero forçar a telefone para girar de volta para Retrato.
Isso não é mais um problema no iPhone 3.1.2 SDK. Agora, parece honrar a orientação solicitada da visão sendo empurrada de volta para a pilha. Isso provavelmente significa que você precisaria detectar versões mais antigas do iPhone OS e aplicar apenas o setOrientation quando estiver antes da última versão.
Não está claro se a análise estática da Apple entenderá que você está trabalhando com as limitações mais antigas do SDK. Pessoalmente, a Apple me disse para remover a chamada do método na minha próxima atualização, então ainda não tenho certeza se ter um hack para dispositivos mais antigos passará pelo processo de aprovação.
Isso é muito depois do fato, mas apenas no caso de alguém aparecer que não esteja usando um controlador de navegação e / ou não queira usar methods não documentados:
UIViewController *c = [[UIViewController alloc]init]; [self presentModalViewController:c animated:NO]; [self dismissModalViewControllerAnimated:NO]; [c release];
É suficiente para apresentar e descartar um controlador de exibição baunilha.
Obviamente, você ainda precisará confirmar ou negar a orientação em sua substituição de shouldAutorotateToInterfaceOrientation. Mas isso fará com que shouldAutorotate … seja chamado novamente pelo sistema.
Se você quiser forçá-lo a girar de retrato para paisagem, aqui está o código. Apenas note que você precisa ajustar o centro da sua visão. Percebi que o meu não colocava a vista no lugar certo. Caso contrário, funcionou perfeitamente. Obrigado pela dica.
if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){ [UIView beginAnimations:@"View Flip" context:nil]; [UIView setAnimationDuration:0.5f]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90)); self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f); self.view.center = CGPointMake(160.0f, 240.0f); [UIView commitAnimations]; }
Pelo que eu posso dizer, o setOrientation:
não funciona (ou talvez não funcione mais). Veja o que estou fazendo para fazer isso:
Primeiro, coloque essa definição no topo do seu arquivo, logo abaixo do #imports:
#define degreesToRadian(x) (M_PI * (x) / 180.0)
então, no método viewWillAppear:
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO]; if (self.interfaceOrientation == UIInterfaceOrientationPortrait) { self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90)); self.view.bounds = CGRectMake(0.0, 0.0, 480, 320); }
Se você quer que seja animado, então você pode envolver tudo em um bloco de animação, assim:
[UIView beginAnimations:@"View Flip" context:nil]; [UIView setAnimationDuration:1.25]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO]; if (self.interfaceOrientation == UIInterfaceOrientationPortrait) { self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90)); self.view.bounds = CGRectMake(0.0, 0.0, 480, 320); } [UIView commitAnimations];
Em seguida, no seu controlador de modo retrato, você pode fazer o inverso – verifique se ele está no modo paisagem e, em caso afirmativo, gire-o de volta para Retrato.
Eu estava tendo um problema onde eu tinha um UIViewController
na canvas, em um UINavigationController
, na orientação paisagem. Quando o próximo controlador de visualização é empurrado no stream, no entanto, precisei que o dispositivo retornasse à orientação de retrato.
O que eu notei foi que o método shouldAutorotateToInterfaceOrientation:
não é chamado quando um novo controlador de visão é colocado na pilha, mas é chamado quando um controlador de visão é retirado da pilha .
Aproveitando isso, estou usando esse snippet de código em um dos meus apps:
- (void)selectHostingAtIndex:(int)hostingIndex { self.transitioning = YES; UIViewController *garbageController = [[[UIViewController alloc] init] autorelease]; [self.navigationController pushViewController:garbageController animated:NO]; [self.navigationController popViewControllerAnimated:NO]; BBHostingController *hostingController = [[BBHostingController alloc] init]; hostingController.hosting = [self.hostings objectAtIndex:hostingIndex]; [self.navigationController pushViewController:hostingController animated:YES]; [hostingController release]; self.transitioning = NO; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { if (self.transitioning) return (toInterfaceOrientation == UIInterfaceOrientationPortrait); else return YES; }
Basicamente, criando um controlador de visão vazio, empurrando-o para a pilha e imediatamente removendo-o, é possível fazer com que a interface volte à posição de retrato. Uma vez que o controle foi triggersdo, eu apenas empurro o controle que eu pretendia empurrar em primeiro lugar. Visualmente, parece ótimo – o controlador de exibição arbitrário e vazio nunca é visto pelo usuário.
Existe uma maneira simples de forçar programaticamente o iPhone para a orientação necessária – usando duas das respostas já fornecidas pelo kdbdallas , Josh :
//will rotate status bar [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight]; //will re-rotate view according to statusbar UIViewController *c = [[UIViewController alloc]init]; [self presentModalViewController:c animated:NO]; [self dismissModalViewControllerAnimated:NO]; [c release];
Funciona como um encanto 🙂
EDITAR:
para iOS 6 eu preciso adicionar esta function: (funciona no viewcontroller modal)
- (NSUInteger)supportedInterfaceOrientations { return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight); }
Eu tenho cavado e cavado procurando por uma boa solução para isso. Encontrei esta postagem do blog que faz o truque: remova sua visualização mais externa da chave UIWindow
e a inclua novamente, o sistema consultará novamente os methods shouldAutorotateToInterfaceOrientation:
de seus viewcontrollers, aplicando a orientação correta a ser aplicada. Veja: iphone forçando uiview para reorientar
A resposta de Josh funciona bem para mim.
No entanto, eu prefiro postar uma notificação de “orientação mudou, por favor, atualize a interface do usuário” . Quando essa notificação é recebida por um controlador de exibição, ela chama shouldAutorotateToInterfaceOrientation:
permitindo que você defina qualquer orientação retornando YES
para a orientação desejada.
[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];
O único problema é que isso força uma reorientação sem uma animação. Você precisaria quebrar essa linha entre beginAnimations:
e commitAnimations
para alcançar uma transição suave.
Espero que ajude.
FWIW, aqui está minha implementação de configurar manualmente a orientação (para entrar no controlador de visão raiz do seu aplicativo, natch):
-(void)rotateInterfaceToOrientation:(UIDeviceOrientation)orientation{ CGRect bounds = [[ UIScreen mainScreen ] bounds ]; CGAffineTransform t; CGFloat r = 0; switch ( orientation ) { case UIDeviceOrientationLandscapeRight: r = -(M_PI / 2); break; case UIDeviceOrientationLandscapeLeft: r = M_PI / 2; break; } if( r != 0 ){ CGSize sz = bounds.size; bounds.size.width = sz.height; bounds.size.height = sz.width; } t = CGAffineTransformMakeRotation( r ); UIApplication *application = [ UIApplication sharedApplication ]; [ UIView beginAnimations:@"InterfaceOrientation" context: nil ]; [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ]; self.view.transform = t; self.view.bounds = bounds; [ UIView commitAnimations ]; [ application setStatusBarOrientation: orientation animated: YES ]; }
juntamente com o seguinte método UINavigationControllerDelegate
(supondo que você esteja usando um UINavigationController
):
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{ // rotate interface, if we need to UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ]; BOOL bViewControllerDoesSupportCurrentOrientation = [ viewController shouldAutorotateToInterfaceOrientation: orientation ]; if( !bViewControllerDoesSupportCurrentOrientation ){ [ self rotateInterfaceToOrientation: UIDeviceOrientationPortrait ]; } }
Isso cuida de rotacionar a visualização da raiz de acordo com o fato de um UIViewController
input suportar a orientação atual do dispositivo. Finalmente, você vai querer ligar o rotateInterfaceToOrientation
às mudanças reais de orientação do dispositivo para imitar a funcionalidade padrão do iOS. Adicione este manipulador de events ao mesmo controlador de visualização raiz:
-(void)onUIDeviceOrientationDidChangeNotification:(NSNotification*)notification{ UIViewController *tvc = self.rootNavigationController.topViewController; UIDeviceOrientation orientation = [[ UIDevice currentDevice ] orientation ]; // only switch if we need to (seem to get multiple notifications on device) if( orientation != [[ UIApplication sharedApplication ] statusBarOrientation ] ){ if( [ tvc shouldAutorotateToInterfaceOrientation: orientation ] ){ [ self rotateInterfaceToOrientation: orientation ]; } } }
Por fim, registre UIDeviceOrientationDidChangeNotification
notifications do UIDeviceOrientationDidChangeNotification
no init
ou no loadview
forma:
[[ NSNotificationCenter defaultCenter ] addObserver: self selector: @selector(onUIDeviceOrientationDidChangeNotification:) name: UIDeviceOrientationDidChangeNotification object: nil ]; [[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
Isso funciona para mim (obrigado Henry Cooke):
O objective para mim era lidar apenas com mudanças de orientações de paisagem.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
- (void)orientationChanged:(NSNotification *)notification { //[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; CGRect bounds = [[ UIScreen mainScreen ] bounds ]; CGAffineTransform t; CGFloat r = 0; switch ( orientation ) { case UIDeviceOrientationLandscapeRight: r = 0; NSLog(@"Right"); break; case UIDeviceOrientationLandscapeLeft: r = M_PI; NSLog(@"Left"); break; default:return; } t = CGAffineTransformMakeRotation( r ); UIApplication *application = [ UIApplication sharedApplication ]; [ UIView beginAnimations:@"InterfaceOrientation" context: nil ]; [ UIView setAnimationDuration: [ application statusBarOrientationAnimationDuration ] ]; self.view.transform = t; self.view.bounds = bounds; [ UIView commitAnimations ]; [ application setStatusBarOrientation: orientation animated: YES ]; }
Eu tenho um aplicativo onde eu gostaria de apoiar a rotação do dispositivo em certas vistas, mas outras não fazem sentido no modo Paisagem, então, ao trocar as vistas, gostaria de forçar a rotação a ser definida para retrato.
Eu percebo que o post original acima neste tópico é muito antigo agora, mas eu tive um problema parecido com ele – ie. Todas as canvass do meu aplicativo são apenas retrato, com exceção de uma canvas, que pode ser alternada entre paisagem e retrato pelo usuário.
Isso era bastante simples, mas, como outros posts, eu queria que o aplicativo retornasse automaticamente ao retrato, independentemente da orientação atual do dispositivo, ao retornar à canvas anterior.
A solução que implementei foi a de ocultar a barra de navegação no modo paisagem, o que significa que o usuário só pode retornar às canvass anteriores enquanto está no retrato. Portanto, todas as outras canvass só podem estar em retrato.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation { BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES; [self.navigationController setNavigationBarHidden:lHideNavBar animated:YES]; }
Isso também tem o benefício adicional do meu aplicativo, pois há mais espaço na canvas disponível no modo paisagem. Isso é útil porque a canvas em questão é usada para exibir arquivos PDF.
Espero que isto ajude.
Eu resolvi isso com bastante facilidade no final. Eu tentei todas as sugestões acima e ainda fiquei aquém, então essa foi a minha solução:
No ViewController que precisa permanecer em Landscape (Esquerda ou Direita), eu ouço mudanças de orientação:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
Então no didRotate:
- (void) didRotate:(NSNotification *)notification { if (orientationa == UIDeviceOrientationPortrait) { if (hasRotated == NO) { NSLog(@"Rotating to portait"); hasRotated = YES; [UIView beginAnimations: @"" context:nil]; [UIView setAnimationDuration: 0]; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-90)); self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f); self.view.frame = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f); [UIView commitAnimations]; } } else if (UIDeviceOrientationIsLandscape( orientationa)) { if (hasRotated) { NSLog(@"Rotating to lands"); hasRotated = NO; [UIView beginAnimations: @"" context:nil]; [UIView setAnimationDuration: 0]; self.view.transform = CGAffineTransformIdentity; self.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(0)); self.view.bounds = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f); self.view.frame = CGRectMake(0.0f, 0.0f, 320.0f, 480.0f); [UIView commitAnimations]; } }
Tenha em mente quaisquer Super Views / Subviews que usam o redimensionamento automático, pois as view.bounds / frame estão sendo redefinidas explicitamente …
A única ressalva a esse método para manter a visão Paisagem é a animação inerente alternando entre as orientações que precisam ocorrer, quando seria melhor que parecesse não haver nenhuma alteração.
Solução iOS 6:
[[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{ [[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil]; }];
O código exato depende por aplicativo e também onde você o coloca (eu usei em meu AppDelegate). Substitua [[self window] rootViewController]
pelo que você usa. Eu estava usando um UITabBarController
.
Eu encontrei uma solução e escrevi algo em francês (mas o código está em inglês). Aqui
O caminho é adicionar o controlador à visualização da janela (o controlador deve possuir uma boa implementação da function shouldRotate ….).
Se você estiver usando UIViewControllers, existe este método:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
Retorna NO
para os controladores de visualização que contêm as vistas que você não deseja girar.
Mais informação aqui
Eu não acho que isso é possível fazer em tempo de execução, mas você poderia, claro, apenas aplicar uma transformação de 90 graus para sua interface do usuário.
Isso é o que eu uso. (Você recebe alguns avisos de compilation, mas funciona tanto no Simulador quanto no iPhone)
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight]; [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];