2012-06-18 77 views
7

我的應用程序支持除了PortraitUpsideDown以外的所有方向。 在我的視圖層次結構中,我有一個AVCaptureVideoPreviewLayer作爲UIImageView的頂層視圖中的子層。然後在視圖下方的層次結構中顯示控件的幾個疊加視圖。如何處理AVCaptureVideoPreviewLayer中的自動旋轉?

覆蓋視圖可以正確使用方向更改,但我不知道如何使用此AVCaptureVideoPreviewLayer。我希望它像Camera應用程序中的行爲一樣,以便previewLayer保持不動,並且控件順利重組。現在,由於主視圖在方向更改上旋轉,所以我的預覽圖層也旋轉了,這意味着在橫向視圖中它保持縱向視圖,僅佔據屏幕的一部分,相機的圖片也旋轉90度。我已經設法手動旋轉預覽圖層,但是它有這個方向改變動畫,這導致在動畫期間看到背景一段時間。

那麼在使previewLayer保持靜止的同時自動控制控件的正確方法是什麼?

+1

曾有類似的問題。一個_obvious_解決方案令人遺憾地不可用:我希望觀察(使用KVO)視圖的圖層'presentationLayer'的轉換,而沒有運氣 - 在動畫運行時鍵值觀察不可用。結束時使用(可能)相同的解決方案:嵌入預覽圖層到一個'UIView'手動旋轉它。 –

+2

是的,我最終還是手動旋轉了包含previewLayer的視圖。事實證明,這不是很複雜。我應用CGAffineTransformMakeRotation(),然後將框架更改爲正確的大小。但現在我完全有我想要的行爲。 – BartoNaz

+0

@BartoNaz我一直在爲此奮鬥了幾天,我所嘗試的一切都沒有給出camera.app的效果,AVCaptureVideoPreviewLayer保持鎖定視圖,並且不會執行動畫旋轉。你能否提供你的工作代碼片段作爲這個問題的答案?謝謝。 –

回答

5

在我的實現中,我已經爲我的視圖子類化了我想要旋轉的UIView,以及像viewController這樣的視圖,它只是NSObject的一個子類。

在這個viewController中,我做了所有與定位變化有關的檢查,決定是否應該改變目標視圖的方向,如果是,那麼我調用我的視圖方法來改變它的方向。

首先,我們需要將整個應用程序界面的方向修改爲肖像模式,以便我們的ACCaptureVideoPreviewLayer始終保持靜止。 這是在做MainViewController.h

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

它NO返回到所有方向,除了人像。

爲了我們的自定義的viewController能夠跟蹤設備的方向,我們需要讓相應的通知觀察員變化:

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

我把這些線在我的viewController的(void)awakeFromNib方法。 所以每當設備方向改變時,viewController的方法orientationChanged將被調用。

其目的是檢查設備的新方向是什麼,設備的最後方向是什麼,並決定是否改變它。這裏是執行:

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

如果方向與以前相同或在PortraitUpsideDown中則不做任何事情。 否則,它會將狀態欄方向設置爲正確的方向,以便在有來電或骨化時它會出現在屏幕的正確一側。然後我在目標視圖中調用方法,其中新方向的所有相應更改都已完成,例如旋轉,調整大小,移動此視圖中與新方向對應的其他界面元素。

這裏是orientationChanged的目標視圖的實現:

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

當然,在這裏你可以添加任何其他更改,如翻譯,那需要應對新的方向和動畫的接口的不同視圖縮放他們。

此外,您可能不需要viewController,但只需在視圖的類中進行操作即可。 希望總體思路清晰。

也不要忘記了方向將更改爲停止收到通知時,你不需要他們喜歡:

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

的主要問題是,當我得到通知,狀態欄沒有旋轉,因此檢查當前值實際上是旋轉前的值。

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

代碼的其餘部分是從BartoNaz一個:因此,我調用一個檢查statusbarorientation和旋轉我的子視圖的方法前加少許延遲(這裏是2秒)。

+0

我不知道這是否正確。這正是這種方法的想法,即狀態欄不會自行旋轉。而且您不檢查狀態欄的當前方向。您正在檢查設備的方向,並將其與上次調用此方法時的方向進行比較。當你的方法決定改變方向時,通過調用'[[UIApplication sharedApplication] setStatusBarOrientation:[UIDevice currentDevice] .orientation animated:NO]手動完成這個改變;' – BartoNaz

+0

你應該使用設備方向而不是狀態欄方向。 –

1

iOS 8的解決方案:

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

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