2012-06-18 15 views
7

Meine Anwendung unterstützt alle Ausrichtungen außer PortraitUpsideDown. In meiner Sichthierarchie habe ich eine AVCaptureVideoPreviewLayer als Sublayer in der Draufsicht, die UIImageView ist. In der Hierarchie darunter befinden sich mehrere Overlay-Ansichten, die Steuerelemente anzeigen.Wie behandelt man Autorotation in AVCaptureVideoPreviewLayer?

Overlay-Ansichten funktionieren ordnungsgemäß mit Orientierungsänderungen, aber ich kann nicht mit dieser AVCaptureVideoPreviewLayer sein. Ich möchte, dass es sich wie in der Kamera-App verhält, so dass previewLayer still bleibt und die Steuerelemente reibungslos reorganisiert werden. Gerade jetzt, da die Hauptansicht bei einer Orientierungsänderung gedreht wird, wird auch meine Vorschauebene gedreht, was bedeutet, dass sie in der Querformatansicht in der Hochformatansicht bleibt, wobei nur ein Teil des Bildschirms und das Bild von der Kamera ebenfalls um 90 Grad gedreht wird. Ich habe es geschafft, den Vorschau-Layer manuell zu drehen, aber dann hat er diese Orientierungsänderungs-Animation, was dazu führt, dass der Hintergrund während der Animation für eine Weile sichtbar ist.

Also, was ist der richtige Weg, um die Kontrollen zu autorotate während previewLayer still bleiben?

+1

Hatte ähnliches Problem. Eine _obvious_ Lösung ist leider nicht verfügbar: ich hatte gehofft (mit KVO) 'transform' von' viewslayer' der Ansichtsschicht ohne Glück zu beobachten - Schlüssel-Wert-Beobachtung ist während der Animationslaufzeit nicht verfügbar. Beendet mit (wahrscheinlich) der gleichen Lösung wie Sie: Preview Layer in eine 'UIView' einbetten und manuell rotieren. –

+2

Ja, ich endete auch mit der manuellen Drehung der Ansicht, die den previewLayer enthält. Es stellte sich heraus, dass es nicht sehr kompliziert war. Ich wenden CGAffineTransformMakeRotation() an und ändern dann den Rahmen auf die richtige Größe. Aber jetzt habe ich genau das Verhalten, das ich will. – BartoNaz

+0

@BartoNaz Ich habe seit ein paar Tagen damit zu kämpfen und alles, was ich versuche, gibt mir nicht den Effekt der camera.app, wo die AVCaptureVideoPreviewLayer auf eine Ansicht gesperrt bleibt, und ihre animierte Rotation nicht macht. Könnten Sie bitte Ihr Arbeitscode-Snippet als Antwort auf diese Frage bereitstellen? Vielen Dank. –

Antwort

5

In meiner Implementierung habe ich die UIView für meine Ansicht subclassiert, die ich drehen möchte und so etwas wie viewController für diese Ansicht, die nur eine Unterklasse von NSObject ist.

In diesem ViewController mache ich alle Prüfungen in Bezug auf Änderungen der Ausrichtung, Entscheidung, ob ich die Ausrichtung meiner Zielansicht ändern sollte, und wenn ja, dann rufe ich Methode meiner Ansicht für die Änderung der Ausrichtung.

Als Erstes müssen wir die Ausrichtung der gesamten Anwendungsoberfläche im Hochformatmodus korrigieren, so dass unsere ACCaptureVideoPreviewLayer immer still bleibt. Dies ist in den MainViewController.h getan:

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

Es gibt NO zu allen Orientierungen außer Portrait.

Um unsere eigenen Viewcontroller der Lage sein, die Änderungen der Geräteausrichtung verfolgen wir einen Beobachter von entsprechenden Mitteilungen machen müssen:

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

ich diese Zeilen in der (void)awakeFromNib Methode meiner Viewcontroller setzen. Jedes Mal, wenn die Geräteausrichtung geändert wird, wird die ViewController-Methode orientationChanged aufgerufen.

Sein Zweck ist es zu überprüfen, was die neue Ausrichtung des Geräts ist, was die letzte Ausrichtung des Geräts war und entscheiden, ob es geändert werden soll. Hier ist die Umsetzung:

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]; 

Wenn die Ausrichtung ist das gleiche wie zuvor oder in PortraitUpsideDown dann nichts zu tun. Andernfalls wird die Statusleistenausrichtung auf die richtige eingestellt, sodass bei einem eingehenden Anruf oder einer Verknöcherung auf der rechten Seite des Bildschirms angezeigt wird. Und dann rufe ich auch method in der Zielansicht auf, wo alle entsprechenden Änderungen für die neue Orientierung vorgenommen werden, wie rotieren, skalieren, die anderen Elemente der Schnittstelle in dieser Ansicht entsprechend der neuen Orientierung verschieben. Hier

ist die Umsetzung des orientationChanged in Zielansicht:

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]; 

Natürlich hier können Sie weitere Änderungen wie Übersetzung hinzufügen, Skalierung verschiedener Ansichten Ihrer Schnittstelle, die neue Ausrichtung reagieren müssen und animieren Sie.

Auch Sie brauchen nicht den ViewController dafür, aber tun Sie alle nur in der Klasse Ihrer Ansicht. Hoffe, dass die allgemeine Idee klar ist.

Vergessen Sie auch nicht immer Benachrichtigung zu stoppen für die Orientierung ändert, wenn Sie sie nicht brauchen mag:

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

Das Hauptproblem ist, dass, wenn ich die Meldung bekommen, hat die Statusbar noch nicht gedreht wird, Wenn Sie also den aktuellen Wert überprüfen, erhalten Sie den Wert vor der Drehung. Also habe ich ein wenig Verzögerung (hier 2 Sekunden) vor dem Aufruf der Methode, die die statusbarorientation überprüfen und dreht mein subview:

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

Der Rest des Codes ist die von BartoNaz.

+0

Ich bin mir nicht sicher, ob das korrekt ist. Das ist genau die Idee dieser Methode, dass die Statusleiste nicht von selbst rotiert. Und Sie überprüfen nicht die aktuelle Ausrichtung der Statusleiste. Sie überprüfen die Ausrichtung des Geräts und vergleichen es mit der Ausrichtung seit dem letzten Aufruf dieser Methode. Und wenn Ihre Methode beschließt, die Ausrichtung zu ändern, ändert sich dies manuell, indem Sie [] [[UIApplication sharedApplication] setStatusBarOrientation: [UIDevice currentDevice] .orientation animiert: NO]; ' – BartoNaz

+0

Hier sollten Sie die Geräteausrichtung statt der Statusleiste verwenden . –

1

iOS 8 Lösung:

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

in Ihrem Setup-Code:

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; 
}