Mam aplikację, w której chciałbym obsługiwać obrót urządzenia w niektórych widokach, ale inne nie mają większego sensu w trybie poziomym, więc zamieniając widoki, chciałbym wymusić ustawienie obrotu na portret.
Istnieje nieudokumentowana metoda ustawiająca właściwości na UIDevice, która załatwia sprawę, ale oczywiście generuje ostrzeżenie kompilatora i może zniknąć wraz z przyszłą wersją zestawu SDK.
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
Czy istnieją udokumentowane sposoby wymuszenia orientacji?
Aktualizacja: Pomyślałem, że podam przykład, ponieważ nie szukam shouldAutorotateToInterfaceOrientation, ponieważ już to zaimplementowałem.
Chcę, aby moja aplikacja obsługiwała orientację poziomą i pionową w widoku 1, ale tylko w widoku pionowym 2. Zaimplementowałem już shouldAutorotateToInterfaceOrientation dla wszystkich widoków, ale jeśli użytkownik jest w trybie poziomym w widoku 1, a następnie przełącza się do widoku 2, chcę wymusić telefon, aby obrócić z powrotem do portretu.
źródło
Odpowiedzi:
Nie stanowi to już problemu w nowszym zestawie SDK telefonu iPhone 3.1.2. Wygląda teraz na to, że przestrzega żądanej orientacji widoku, który jest wypychany z powrotem na stos. Prawdopodobnie oznacza to, że musiałbyś wykryć starsze wersje systemu iPhone OS i zastosować setOrientation tylko wtedy, gdy jest przed najnowszą wersją.
Nie jest jasne, czy statyczna analiza Apple zrozumie, że omijasz starsze ograniczenia SDK. Osobiście otrzymałem od firmy Apple polecenie usunięcia wywołania metody przy następnej aktualizacji, więc nie jestem jeszcze pewien, czy włamanie do starszych urządzeń przejdzie przez proces zatwierdzania.
źródło
Dzieje się to długo po fakcie, ale na wypadek, gdyby pojawił się ktoś, kto nie używa kontrolera nawigacji i / lub nie chce używać nieudokumentowanych metod:
UIViewController *c = [[UIViewController alloc]init]; [self presentModalViewController:c animated:NO]; [self dismissModalViewControllerAnimated:NO]; [c release];
Wystarczy przedstawić i odrzucić zwykły kontroler widoku.
Oczywiście nadal będziesz musiał potwierdzić lub zaprzeczyć orientacji w swoim zastąpieniu shouldAutorotateToInterfaceOrientation. Ale to spowoduje, że powinienAutorotate ... zostanie ponownie wywołany przez system.
źródło
Jeśli chcesz zmusić go do obracania się z pionu na poziomo, oto kod. Zwróć uwagę, że musisz dostosować środek widoku. Zauważyłem, że mój nie umieścił widoku we właściwym miejscu. W przeciwnym razie działało idealnie. Dzięki za wskazówkę.
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]; }
źródło
CGAffineTransformMakeRotation
obrócić widok, ale UIAlertView nadal jest zgodny z orientacją paska stanu, tj. -DegreeToRadian (kąt)? Dostałeś coś takiego?UIAlertView
jest ustawienie kontrolera widoku jako delegata alertView, a następnie w:,- (void) didPresentAlertView:(UIAlertView *) alertView
wykonujesz rotację na podstawie bieżącej[UIApplication sharedApplication].statusBarOrientation
. Jeśli go nie animujesz, nie powinno wyglądać zbyt dziwnie.Z tego, co wiem,
setOrientation:
metoda nie działa (lub może już nie działa). Oto, co robię, aby to zrobić:najpierw umieść tę definicję na górze pliku, tuż pod #imports:
#define degreesToRadian(x) (M_PI * (x) / 180.0)
następnie w
viewWillAppear:
metodzie[[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); }
jeśli chcesz, aby był animowany, możesz zawinąć całość w blok animacji, na przykład:
[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];
Następnie w kontrolerze trybu portretu możesz zrobić odwrotnie - sprawdź, czy jest obecnie w poziomie, a jeśli tak, obróć go z powrotem do portretu.
źródło
Miałem problem z ikoną
UIViewController
na ekranie, wUINavigationController
orientacji poziomej. Kiedy jednak kolejny kontroler widoku zostanie wciśnięty w przepływ, potrzebowałem, aby urządzenie powróciło do orientacji pionowej.Zauważyłem, że
shouldAutorotateToInterfaceOrientation:
metoda nie jest wywoływana, gdy nowy kontroler widoku jest umieszczany na stosie, ale jest wywoływana, gdy kontroler widoku jest zdejmowany ze stosu .Korzystając z tego, używam tego fragmentu kodu w jednej z moich aplikacji:
- (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; }
Zasadniczo, tworząc pusty kontroler widoku, umieszczając go na stosie i natychmiast usuwając, możliwe jest przywrócenie interfejsu do pozycji pionowej. Po wyskoczeniu kontrolera po prostu wciskam kontroler, który zamierzałem nacisnąć w pierwszej kolejności. Wizualnie wygląda świetnie - użytkownik nigdy nie widzi pustego, dowolnego kontrolera widoku.
źródło
Istnieje prosty sposób, aby programowo zmusić iPhone'a do odpowiedniej orientacji - korzystając z dwóch odpowiedzi udzielonych już przez kdbdallasa , Josha :
//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];
działa jak marzenie :)
EDYTOWAĆ:
dla iOS 6 muszę dodać tę funkcję: (działa na modalnym kontrolerze widoku)
- (NSUInteger)supportedInterfaceOrientations { return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight); }
źródło
Kopałem i kopałem, szukając dobrego rozwiązania tego problemu. Znalazłem ten post na blogu, który załatwia
UIWindow
sprawę : usuń najbardziej zewnętrzny widok z klucza i dodaj go ponownie, a system ponownie wyśle zapytanie doshouldAutorotateToInterfaceOrientation:
metod z kontrolerów widoku, wymuszając prawidłową orientację do zastosowania. Zobacz: iphone wymusza zmianę orientacji uiviewźródło
Odpowiedź Josha działa dla mnie dobrze.
Jednak wolę publikować powiadomienie „orientacja się zmieniła, zaktualizuj interfejs użytkownika” . Gdy to powiadomienie zostanie odebrane przez kontroler widoku, wywołuje
shouldAutorotateToInterfaceOrientation:
, umożliwiając ustawienie dowolnej orientacji poprzez powrótYES
do żądanej orientacji.[[NSNotificationCenter defaultCenter] postNotificationName:UIDeviceOrientationDidChangeNotification object:nil];
Jedynym problemem jest to, że wymusza to zmianę orientacji bez animacji. Aby uzyskać płynne przejście, musisz zawinąć tę linię między
beginAnimations:
icommitAnimations
.Mam nadzieję, że to pomoże.
źródło
FWIW, oto moja implementacja ręcznego ustawiania orientacji (aby przejść do kontrolera widoku głównego aplikacji, 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 ]; }
w połączeniu z następującą
UINavigationControllerDelegate
metodą (zakładając, że używasz aUINavigationController
):-(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 ]; } }
Dba to o obrócenie widoku głównego w zależności od tego, czy przychodzące
UIViewController
obsługuje bieżącą orientację urządzenia. Na koniec będziesz chciał podłączyć sięrotateInterfaceToOrientation
do rzeczywistych zmian orientacji urządzenia, aby naśladować standardowe funkcje iOS. Dodaj tę procedurę obsługi zdarzeń do tego samego głównego kontrolera widoku:-(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 ]; } } }
Wreszcie zarejestrować
UIDeviceOrientationDidChangeNotification
zawiadomieńinit
lubloadview
tak:[[ NSNotificationCenter defaultCenter ] addObserver: self selector: @selector(onUIDeviceOrientationDidChangeNotification:) name: UIDeviceOrientationDidChangeNotification object: nil ]; [[ UIDevice currentDevice ] beginGeneratingDeviceOrientationNotifications ];
źródło
shouldAutorotate
metodą (patrz stackoverflow.com/a/12586002/5171 ).To działa dla mnie (dziękuję Henry Cooke):
Moim celem było zajmowanie się tylko zmianami orientacji krajobrazu.
metoda init:
[[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 ]; }
źródło
Zdaję sobie sprawę, że powyższy oryginalny post w tym wątku jest teraz bardzo stary, ale miałem z nim podobny problem - tj. wszystkie ekrany w mojej aplikacji są tylko w orientacji pionowej, z wyjątkiem jednego ekranu, który użytkownik może obracać między orientacją poziomą a pionową.
Było to dość proste, ale podobnie jak inne posty, chciałem, aby aplikacja automatycznie powracała do portretu, niezależnie od aktualnej orientacji urządzenia, po powrocie do poprzedniego ekranu.
Rozwiązaniem, które zaimplementowałem, było ukrycie paska nawigacji w trybie poziomym, co oznacza, że użytkownik może powrócić do poprzednich ekranów tylko w trybie pionowym. Dlatego wszystkie inne ekrany mogą być wyświetlane tylko w orientacji pionowej.
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)pInterfaceOrientation { BOOL lHideNavBar = self.interfaceOrientation == UIInterfaceOrientationPortrait ? NO : YES; [self.navigationController setNavigationBarHidden:lHideNavBar animated:YES]; }
Ma to również dodatkową zaletę dla mojej aplikacji, ponieważ w trybie poziomym jest więcej miejsca na ekranie. Jest to przydatne, ponieważ dany ekran służy do wyświetlania plików PDF.
Mam nadzieję że to pomoże.
źródło
Ostatecznie rozwiązałem to dość łatwo. Wypróbowałem każdą sugestię powyżej i nadal nie wychodziłem, więc to było moje rozwiązanie:
W kontrolerze ViewController, który musi pozostać poziomy (lewy lub prawy), nasłuchuję zmian orientacji:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
Następnie w 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]; } }
Należy pamiętać o wszelkich superwidokach / podglądzie, które używają autoregulacji, ponieważ widok.bounds / frame są resetowane jawnie ...
Jedynym zastrzeżeniem tej metody zachowania widoku Krajobraz jest nieodłączna animacja przełączania między orientacjami, która musi wystąpić, kiedy lepiej byłoby, aby wyglądało na to, że nie zmienia się.
źródło
Rozwiązanie iOS 6:
[[[self window] rootViewController] presentViewController:[[UIViewController alloc] init] animated:NO completion:^{ [[[self window] rootViewController] dismissViewControllerAnimated:NO completion:nil]; }];
Dokładny kod zależy od aplikacji, a także od miejsca, w którym ją umieścisz (użyłem go w moim AppDelegate). Zastąp
[[self window] rootViewController]
to, czego używasz. UżywałemUITabBarController
.źródło
Znalazłem rozwiązanie i napisałem coś po francusku (ale kod jest po angielsku). tutaj
Sposobem jest dodanie kontrolera do widoku okna (kontroler musi mieć dobrą implementację funkcji shouldRotate ....).
źródło
Jeśli używasz UIViewControllers, istnieje ta metoda:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
Wróć
NO
do kontrolerów widoku zawierających widoki, których nie chcesz obracać.Więcej informacji tutaj
źródło
Nie sądzę, aby było to możliwe w czasie wykonywania, chociaż oczywiście można po prostu zastosować transformację 90 stopni w swoim interfejsie użytkownika.
źródło
To jest to, czego używam. (Otrzymujesz ostrzeżenia dotyczące kompilacji, ale działa to zarówno w symulatorze, jak i na iPhonie)
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight]; [[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
źródło