AVCaptureVideoPreviewLayer 正确显示 iPhone6 除外
AVCaptureVideoPreviewLayer displays correctly except on iPhone6
我正在努力实现一个包含 AVCaptureVideoPreviewLayer 的视图。视图应该占据整个屏幕,顶部的导航栏除外(视图嵌入在导航控制器中)。
下面的代码显示了我如何设置捕获会话并添加前置摄像头作为输入(如果可用)。
//Set up capture Session
session = [[AVCaptureSession alloc]init];
session.sessionPreset = AVCaptureSessionPresetPhoto;
//Add a camera as input if we have a front-facing camera available
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSError *error;
for (AVCaptureDevice *device in devices) {
if ([device position] == AVCaptureDevicePositionBack) {
//Set our input
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
[session addInput:input];
if (!input) {
NSLog(@"No Input");
}
}
}
下面是配置输出的地方,相机输出视图被添加为子视图,框架由视图的边界决定。
//Output
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:output];
output.videoSettings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
//Preview Layer
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
previewLayer.frame = self.view.bounds;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:previewLayer];
正如您在下面的屏幕截图中看到的,当 运行 在 iPhone 6 上时,预览层不会占据整个屏幕(白色 space 到相机视图右侧和导航栏下方应由预览层填充):
但是,它确实在下面的 iPhone 5 屏幕截图(以及 iPhone 4、4S 和 5S)中适当地填满了屏幕。有人知道为什么吗?
假设您在 viewDidLoad
中创建此视频预览层,视图控制器的视图尚未布局,因此其 frame
和 bounds
不一定一旦视图显示在屏幕上,它们将是什么。您必须等到视图布局完成后才能设置预览层的正确框架,您可以通过实现视图控制器的 viewDidLayoutSubviews
方法来实现这一点,如下所示:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.previewLayer.frame = self.view.bounds;
}
这可以确保任何时候您的视图控制器的视图大小发生变化,您的预览层的大小也会发生变化。
您可以使用 AVCaptureVideoPreviewLayer
作为 UIView
子类的支持层,然后将此视图的实例作为子视图添加到视图控制器的视图中。通过这种方式,您可以根据需要使用约束来定位视频捕获预览,就像在任何其他视图中一样。
以下是您如何执行此操作的示例:
// MyCaptureVideoPreviewView.h
#import <AVFoundation/AVFoundation.h>
@interface MyCaptureVideoPreviewView : UIView
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *layer;
@property (nonatomic) AVCaptureSession *session;
- (instancetype)initWithFrame:(CGRect)frame session:(AVCaptureSession *)session;
@end
// MyCaptureVideoPreviewView.m
@implementation MyCaptureVideoPreviewView
@dynamic layer;
+ (Class)layerClass {
return [AVCaptureVideoPreviewLayer class];
}
- (AVCaptureSession *)session {
return self.layer.session;
}
- (void)setSession:(AVCaptureSession *)session {
self.layer.session = session;
}
- (instancetype)initWithFrame:(CGRect)frame session:(AVCaptureSession *)session {
self = [super initWithFrame:frame];
if (self) {
self.session = session;
}
return self;
}
@end
然后在您的视图控制器实现中的某处:
MyCaptureVideoPreviewView *previewView = [[MyCaptureVideoPreviewView alloc] initWithFrame:self.view.bounds
session:session];
previewView.layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:previewView];
// Add whatever constraints you want. Here we just match the superview's bounds:
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[previewView]|" options:kNilOptions
metrics:nil views:NSDictionaryOfVariableBindings(previewView)];
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[previewView]|" options:kNilOptions
metrics:nil views:NSDictionaryOfVariableBindings(previewView)];
[self.view addConstraints:vConstraints];
[self.view addConstraints:hConstraints];
我正在努力实现一个包含 AVCaptureVideoPreviewLayer 的视图。视图应该占据整个屏幕,顶部的导航栏除外(视图嵌入在导航控制器中)。
下面的代码显示了我如何设置捕获会话并添加前置摄像头作为输入(如果可用)。
//Set up capture Session
session = [[AVCaptureSession alloc]init];
session.sessionPreset = AVCaptureSessionPresetPhoto;
//Add a camera as input if we have a front-facing camera available
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSError *error;
for (AVCaptureDevice *device in devices) {
if ([device position] == AVCaptureDevicePositionBack) {
//Set our input
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
[session addInput:input];
if (!input) {
NSLog(@"No Input");
}
}
}
下面是配置输出的地方,相机输出视图被添加为子视图,框架由视图的边界决定。
//Output
AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init];
[session addOutput:output];
output.videoSettings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
//Preview Layer
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];
previewLayer.frame = self.view.bounds;
self.view.translatesAutoresizingMaskIntoConstraints = NO;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:previewLayer];
正如您在下面的屏幕截图中看到的,当 运行 在 iPhone 6 上时,预览层不会占据整个屏幕(白色 space 到相机视图右侧和导航栏下方应由预览层填充):
但是,它确实在下面的 iPhone 5 屏幕截图(以及 iPhone 4、4S 和 5S)中适当地填满了屏幕。有人知道为什么吗?
假设您在 viewDidLoad
中创建此视频预览层,视图控制器的视图尚未布局,因此其 frame
和 bounds
不一定一旦视图显示在屏幕上,它们将是什么。您必须等到视图布局完成后才能设置预览层的正确框架,您可以通过实现视图控制器的 viewDidLayoutSubviews
方法来实现这一点,如下所示:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.previewLayer.frame = self.view.bounds;
}
这可以确保任何时候您的视图控制器的视图大小发生变化,您的预览层的大小也会发生变化。
您可以使用 AVCaptureVideoPreviewLayer
作为 UIView
子类的支持层,然后将此视图的实例作为子视图添加到视图控制器的视图中。通过这种方式,您可以根据需要使用约束来定位视频捕获预览,就像在任何其他视图中一样。
以下是您如何执行此操作的示例:
// MyCaptureVideoPreviewView.h
#import <AVFoundation/AVFoundation.h>
@interface MyCaptureVideoPreviewView : UIView
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *layer;
@property (nonatomic) AVCaptureSession *session;
- (instancetype)initWithFrame:(CGRect)frame session:(AVCaptureSession *)session;
@end
// MyCaptureVideoPreviewView.m
@implementation MyCaptureVideoPreviewView
@dynamic layer;
+ (Class)layerClass {
return [AVCaptureVideoPreviewLayer class];
}
- (AVCaptureSession *)session {
return self.layer.session;
}
- (void)setSession:(AVCaptureSession *)session {
self.layer.session = session;
}
- (instancetype)initWithFrame:(CGRect)frame session:(AVCaptureSession *)session {
self = [super initWithFrame:frame];
if (self) {
self.session = session;
}
return self;
}
@end
然后在您的视图控制器实现中的某处:
MyCaptureVideoPreviewView *previewView = [[MyCaptureVideoPreviewView alloc] initWithFrame:self.view.bounds
session:session];
previewView.layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:previewView];
// Add whatever constraints you want. Here we just match the superview's bounds:
NSArray *vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[previewView]|" options:kNilOptions
metrics:nil views:NSDictionaryOfVariableBindings(previewView)];
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[previewView]|" options:kNilOptions
metrics:nil views:NSDictionaryOfVariableBindings(previewView)];
[self.view addConstraints:vConstraints];
[self.view addConstraints:hConstraints];