Objective c 如何在AVCaptureVideoPreviewLayer中处理自动旋转?

Objective c 如何在AVCaptureVideoPreviewLayer中处理自动旋转?,objective-c,ios,orientation,preview,autorotate,Objective C,Ios,Orientation,Preview,Autorotate,我的应用程序支持除上下方向外的所有方向。 在我的视图层次结构中,我有一个AVCaptureVideoPreviewLayer作为顶视图(即UIImageView)中的子层。然后在视图层次结构的下面是几个显示控件的覆盖视图 覆盖视图在方向改变时工作正常,但我不知道如何使用这个AVCaptureVideoPreviewLayer。我希望它的行为像照相机应用程序一样,这样previewLayer就可以保持静止,并且可以平滑地重新组织控件。现在,由于主视图在方向改变时旋转,我的预览层也会旋转,这意味着在

我的应用程序支持除上下方向外的所有方向。 在我的视图层次结构中,我有一个AVCaptureVideoPreviewLayer作为顶视图(即UIImageView)中的子层。然后在视图层次结构的下面是几个显示控件的覆盖视图

覆盖视图在方向改变时工作正常,但我不知道如何使用这个AVCaptureVideoPreviewLayer。我希望它的行为像照相机应用程序一样,这样previewLayer就可以保持静止,并且可以平滑地重新组织控件。现在,由于主视图在方向改变时旋转,我的预览层也会旋转,这意味着在横向视图中,它保持在纵向视图中,只拍摄部分屏幕,相机的图片也旋转90度。我已经设法手动旋转预览层,但是它有一个方向改变动画,这会导致在动画中看到背景一段时间


那么,在使previewLayer保持静止的同时,自动旋转控件的正确方法是什么呢?

在我的实现中,我为要旋转的视图对UIView进行了子类化,并为该视图对viewController进行了类似的子类化,它只是NSObject的一个子类

在此viewController中,我执行与方向更改相关的所有检查,决定是否应更改目标视图的方向,如果是,则调用视图的方法来更改其方向

首先,我们需要将整个应用程序界面的方向固定为纵向模式,以便ACCaptureVideoPreviewLayer始终保持静止。 这是在
MainViewController.h
中完成的:

(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation`
{
    return interfaceOrientation==UIInterfaceOrientationPortrait;
}
它对除“纵向”之外的所有方向都返回“否”

为了使我们的自定义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];
如果方向与之前相同或朝上向下,则不执行任何操作。 否则,它会将状态栏的方向设置为正确的方向,以便当有来电或骨化时,它会显示在屏幕的正确一侧。然后我还调用了目标视图中的方法,在目标视图中对新方向进行了所有相应的更改,比如旋转、调整大小、移动该视图中与新方向相对应的界面的其他元素

以下是在目标视图中更改的
方向的实现:

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

主要问题是,当我收到通知时,状态栏还没有旋转,因此检查当前值实际上给出了旋转前的值。因此,在调用检查StatusBaroOrientation和旋转子视图的方法之前,我添加了一点延迟(此处为2秒):

-(void) handleNotification:(NSNotification *) notification
{    
    (void) [NSTimer scheduledTimerWithTimeInterval:(2.0)
                                           target:self
                                          selector:@selector(orientationChanged)
                                          userInfo:nil
                                           repeats:NO ] ;
}
其余代码来自BartoNaz。

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

也有类似的问题。遗憾的是,一个明显的解决方案不可用:我希望在没有运气的情况下观察(使用KVO)
transform
视图层的
presentationLayer
——关键值观察在动画运行时不可用。最终使用(可能)与您相同的解决方案:将预览层嵌入到
UIView
a手动旋转它。是的,我还手动旋转了包含预览层的视图。结果不是很复杂。我应用CGAffineTransformMakeRotation(),然后将帧更改为正确的大小。但是现在我完全有了我想要的行为。@BartoNaz我已经为此挣扎了几天,我尝试的一切都没有给我camera.app的效果,AVCaptureVideoPreviewLayer锁定在一个视图上,并且没有进行动画旋转。您能提供您的工作代码片段作为这个问题的答案吗?谢谢。巴托纳兹,我也被困在同一个问题上,请在下面提供你的解决方案作为回答。我不确定这是否正确。这正是该方法的思想,状态栏不会自行旋转。并且不检查状态栏的当前方向。您正在检查设备的方向,并将其与上次调用此方法时的方向进行比较。当您的方法决定更改方向时,它会通过调用
[[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice]。方向动画:否]手动进行更改此处应使用设备方向,而不是状态栏方向。
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;
}