2012-06-18 31 views
7

Moja aplikacja obsługuje wszystkie orientacje z wyjątkiem PortraitUpsideDown. W mojej hierarchii widoków mam AVCaptureVideoPreviewLayer jako podwarstwę w widoku z góry, która jest UIImageView. Następnie w hierarchii widoków znajduje się kilka widoków nakładek z kontrolkami.Jak radzić sobie z autorotacją w AVCaptureVideoPreviewLayer?

Widok nakładki działa poprawnie przy zmianach orientacji, ale nie wiem, jak to zrobić z tym obiektem AVCaptureVideoPreviewLayer. Chcę, aby zachowywał się tak, jak w aplikacji Aparat, aby podglądLayer pozostał nieruchomy, a elementy sterujące zostały płynnie zreorganizowane. W tej chwili, ponieważ widok główny obraca się podczas zmiany orientacji, moja warstwa podglądu jest również obracana, co oznacza, że ​​w widoku poziomym pozostaje ona w widoku pionowym, zajmując tylko część ekranu, a obraz z kamery również obraca się o 90 stopni. Udało mi się ręcznie obrócić warstwę podglądu, ale potem ma ona animację zmiany orientacji, która prowadzi do tego, że tło będzie widoczne przez jakiś czas podczas animacji.

Jaki jest właściwy sposób autorotacji elementów sterujących podczas tworzenia podglądu?

+1

Gdyby similiar problem. Jedno oczywiste rozwiązanie jest niestety niedostępne: miałem nadzieję zaobserwować (za pomocą KVO) 'transformację' warstwy widoku 'presentationLayer' bez powodzenia - obserwacja wartości klucz-wartość nie jest dostępna podczas wykonywania animacji. Zakończono używanie (prawdopodobnie) tego samego rozwiązania, co ty: osadzanie warstwy podglądu w "UIView" i ręczne obracanie go. –

+2

Tak, również skończyłem z ręcznym obróceniem widoku zawierającego previewLayer. Okazało się, że nie jest to zbyt skomplikowane. Stosuję CGAffineTransformMakeRotation(), a następnie zmieniam ramkę na prawidłowy rozmiar. Ale teraz mam dokładnie to, czego pragnę. – BartoNaz

+0

@BartoNaz Miałem problemy z tym od kilku dni i wszystko, co próbuję, nie daje mi efektu camera.app, gdzie AVCaptureVideoPreviewLayer pozostaje zablokowany na widok i nie wykonuje animowanej rotacji. Czy możesz podać swój działający fragment kodu jako odpowiedź na to pytanie? Dzięki. –

Odpowiedz

5

W mojej implementacji podklasowałem UIView dla mojego widoku, który chcę obrócić i coś jak viewController dla tego widoku, który jest tylko podklasą NSObject.

W tym viewController robię wszystkie kontrole związane ze zmianami orientacji, podejmie decyzję, czy należy zmienić orientację moim zdaniem docelowej, a jeśli tak, to zgłoszę metoda moim zdaniem do zmiany jej orientacji.

Przede wszystkim musimy ustawić orientację całego interfejsu aplikacji na tryb portretowy, aby nasza ACCaptureVideoPreviewLayer zawsze pozostawała nieruchoma. Odbywa się to w MainViewController.h:

(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation` 
{ 
    return interfaceOrientation==UIInterfaceOrientationPortrait; 
} 

To nie zwraca się do wszystkich kierunkach z wyjątkiem portret.

W celu naszego zwyczaju viewController móc śledzić zmiany orientacji urządzenia musimy dokonać jej obserwatora odpowiednich powiadomień:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil]; 

mogę umieścić te linie w sposobie mojego viewController (void)awakeFromNib. Za każdym razem, gdy zmieni się orientacja urządzenia, zostanie wywołana metoda viewController, orientationChanged.

Jego celem jest sprawdzenie, jaka jest nowa orientacja urządzenia, jaka była ostatnia orientacja urządzenia i podjęcie decyzji o jego zmianie. Oto realizacja:

if(UIInterfaceOrientationPortraitUpsideDown==[UIDevice currentDevice].orientation || 
    lastOrientation==(UIInterfaceOrientation)[UIDevice currentDevice].orientation) 
    return; 
    [[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice].orientation animated:NO]; 

    lastOrientation=[UIApplication sharedApplication].statusBarOrientation; 
    [resultView orientationChanged]; 

Jeśli orientacja jest taka sama jak poprzednio lub w PortraitUpsideDown, to nic nie rób. Inaczej ustawia orientację paska stanu na właściwą, dzięki czemu w przypadku połączenia przychodzącego lub kostnienia pojawi się po właściwej stronie ekranu. Następnie wywołuję metodę również w widoku docelowym, gdzie wykonuje się wszystkie odpowiednie zmiany dla nowej orientacji, takie jak obracanie, zmiana rozmiaru, przesuwanie innych elementów interfejsu w tym widoku, odpowiadających nowej orientacji.

Oto realizacja orientationChanged w widoku docelowym:

Float32 angle=0.f; 
UIInterfaceOrientation orientation=[UIApplication sharedApplication].statusBarOrientation; 

switch (orientation) { 
    case UIInterfaceOrientationLandscapeLeft: 
     angle=-90.f*M_PI/180.f; 
     break; 
    case UIInterfaceOrientationLandscapeRight: 
      angle=90.f*M_PI/180.f; 
     break; 
    default: angle=0.f; 
     break; 
} 
if(angle==0 && CGAffineTransformIsIdentity(self.transform)) return; 

CGAffineTransform transform=CGAffineTransformMakeRotation(angle); 

[UIView beginAnimations:@"rotateView" context:nil]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 
[UIView setAnimationDuration:0.35f]; 

self.transform=transform; 

[UIView commitAnimations]; 

Oczywiście tu można dodawać żadnych innych zmian, takich jak tłumaczenia, skalowanie różnych widoków interfejsu, które muszą odpowiedzieć na nowej orientacji i animować im.

także może nie trzeba się viewController za to, ale zrobić wszystko tylko w klasie widoku. Mam nadzieję, że ogólny pomysł jest jasny.

również nie zapomnieć, by przestać otrzymywać powiadomienia o zmianie orientacji, gdy nie trzeba ich jak:

[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; 
[[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications]; 
0

Głównym problemem jest to, że kiedy pojawia się powiadomienie, statusbar jeszcze nie obraca się, więc sprawdzenie aktualnej wartości daje w rzeczywistości wartość przed obrotem. Więc dodałem trochę opóźnienia (tu 2 sekund) przed wywołaniem że sprawdzenie statusbarorientation i obraca mój podrzędny metodę:

-(void) handleNotification:(NSNotification *) notification 
{  
    (void) [NSTimer scheduledTimerWithTimeInterval:(2.0) 
              target:self 
              selector:@selector(orientationChanged) 
              userInfo:nil 
              repeats:NO ] ; 
} 

Reszta kodu jest jedną z BartoNaz.

+0

Nie jestem pewien, czy to jest poprawne. Dokładnie taka jest idea tej metody, że pasek stanu nie obraca się samoczynnie. I nie sprawdzasz bieżącej orientacji paska stanu. Sprawdzasz orientację urządzenia i porównujesz je z orientacją od ostatniego wywołania tej metody. A gdy metoda postanawia zmienić orientację, czyni tę zmianę ręcznie, poprzez wywołanie '[[UIApplication sharedApplication] setStatusBarOrientation: [UIDevice currentDevice] .orientation animowany: NO];' – BartoNaz

+0

Należy używać orientacji urządzenia zamiast statusu orientacji bar tutaj . –

1

iOS 8 rozwiązanie:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { 
    if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { 
     self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 
    } else { 
     self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 
    } 
} 

w kodzie konfiguracji:

self.layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session]; 
self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill; 
if ([UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft) { 
    self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeLeft; 
} else { 
    self.layer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; 
} 
Powiązane problemy