【问题标题】:How to handle autorotation in AVCaptureVideoPreviewLayer?如何处理 AVCaptureVideoPreviewLayer 中的自动旋转?
【发布时间】:2012-06-18 09:04:50
【问题描述】:

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

叠加视图可以在方向更改时正常工作,但我不知道如何使用此 AVCaptureVideoPreviewLayer。我希望它的行为像在相机应用程序中一样,以便 previewLayer 保持静止并且控件可以顺利重组。现在,由于主视图在方向更改时旋转,我的预览层也旋转了,这意味着在横向视图中它保持纵向视图,只占据屏幕的一部分,并且来自相机的图片也旋转了 90 度。我已经设法手动旋转预览层,但是它有这个方向改变动画,这导致在动画期间可以看到背景一段时间。

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

【问题讨论】:

  • 有类似的问题。遗憾的是,一个 明显 解决方案不可用:我希望观察(使用 KVO)transform 的视图层的 presentationLayer 没有运气 - 键值观察在动画运行时不可用。最终使用(可能)与您相同的解决方案:将预览层嵌入到 UIView 中并手动旋转它。
  • 是的,我还手动旋转了包含 previewLayer 的视图。结果并不是很复杂。我应用 CGAffineTransformMakeRotation() 然后将框架更改为正确的大小。但现在我有了我想要的行为。
  • @BartoNaz 我已经为此苦苦挣扎了几天,我尝试的一切都没有给我 camera.app 的效果,其中 AVCaptureVideoPreviewLayer 保持锁定在视图中,并且没有t 进行动画旋转。您能否提供您的工作代码 sn-p 作为这个问题的答案?谢谢。
  • BartoNaz 我也遇到了同样的问题,请在下面提供您的解决方案作为答案

标签: objective-c ios orientation preview autorotate


【解决方案1】:

在我的实现中,我将 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];

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      主要问题是当我收到通知时,状态栏还没有旋转,所以检查当前值实际上是旋转前的值。所以我在调用检查状态栏方向并旋转我的子视图的方法之前添加了一点延迟(这里是 2 秒):

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

      其余代码来自 BartoNaz。

      【讨论】:

      • 我不确定这是否正确。这正是这种方法的想法,状态栏不会自行旋转。而且您不会检查状态栏的当前方向。您正在检查设备的方向,并将其与上次调用此方法时的方向进行比较。当你的方法决定改变方向时,它会通过调用[[UIApplication sharedApplication]setStatusBarOrientation:[UIDevice currentDevice].orientation animated:NO]; 手动进行更改
      • 您应该在这里使用设备方向而不是状态栏方向。
      猜你喜欢
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多