我们在自定义相机时,若要实现镜头变焦,也就是推近或者拉远焦距,有两种方法可以实现:可以通过修改AVCaptureDevice
的缩放系数videoZoomFactor
来实现镜头变焦,也可以通过修改AVCaptureConnection
的缩放系数videoScaleAndCropFactor
来实现镜头变焦。
1、修改AVCaptureDevice
的缩放系数videoZoomFactor
通过修改AVCaptureDevice
的缩放系数videoZoomFactor
来实现镜头变焦的核心代码如下:
//最小缩放值
- (CGFloat)minZoomFactor
{
CGFloat minZoomFactor = 1.0;
if (@available(iOS 11.0, *)) {
minZoomFactor = self.device.minAvailableVideoZoomFactor;
}
return minZoomFactor;
}
//最大缩放值
- (CGFloat)maxZoomFactor
{
CGFloat maxZoomFactor = self.device.activeFormat.videoMaxZoomFactor;
if (@available(iOS 11.0, *)) {
maxZoomFactor = self.device.maxAvailableVideoZoomFactor;
}
if (maxZoomFactor > 6.0) {
maxZoomFactor = 6.0;
}
return maxZoomFactor;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]){
self.slider.minimumValue = self.minZoomFactor;
self.slider.maximumValue = self.maxZoomFactor;
self.currentZoomFactor = self.device.videoZoomFactor;
}
return YES;
}
//缩放手势
- (void)zoomChangePinchGestureRecognizerClick:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
if (pinchGestureRecognizer.state == UIGestureRecognizerStateBegan ||
pinchGestureRecognizer.state == UIGestureRecognizerStateChanged)
{
CGFloat currentZoomFactor = self.currentZoomFactor * pinchGestureRecognizer.scale;
self.slider.hidden = NO;
if (currentZoomFactor < self.maxZoomFactor &&
currentZoomFactor > self.minZoomFactor){
NSError *error = nil;
if ([self.device lockForConfiguration:&error] ) {
self.device.videoZoomFactor = currentZoomFactor;
self.slider.value = self.device.videoZoomFactor;
[self.device unlockForConfiguration];
}
else {
NSLog( @"Could not lock device for configuration: %@", error );
}
}
}
else
{
self.slider.hidden = YES;
}
}
- (void)sliderValueChangeClick:(UISlider *)sender
{
self.slider.hidden = NO;
if (sender.value < self.maxZoomFactor &&
sender.value > self.minZoomFactor){
NSError *error = nil;
if ([self.device lockForConfiguration:&error] ) {
self.device.videoZoomFactor = sender.value;
[self.device unlockForConfiguration];
}
else {
NSLog( @"Could not lock device for configuration: %@", error );
}
}
}
以上这么多代码,核心代码只有一处,通过该句代码设置了新的缩放比例:
self.device.videoZoomFactor = currentZoomFactor;
2、修改AVCaptureConnection
的缩放系数videoScaleAndCropFactor
通过修改AVCaptureConnection
的缩放系数videoScaleAndCropFactor
来实现镜头变焦的核心代码如下:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ( [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] ) {
beginGestureScale = effectiveScale;
}
return YES;
}
// scale image depending on users pinch gesture
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer
{
BOOL allTouchesAreOnThePreviewLayer = YES;
NSUInteger numTouches = [recognizer numberOfTouches], i;
for ( i = 0; i < numTouches; ++i ) {
CGPoint location = [recognizer locationOfTouch:i inView:previewView];
CGPoint convertedLocation = [previewLayer convertPoint:location fromLayer:previewLayer.superlayer];
if ( ! [previewLayer containsPoint:convertedLocation] ) {
allTouchesAreOnThePreviewLayer = NO;
break;
}
}
if ( allTouchesAreOnThePreviewLayer ) {
effectiveScale = beginGestureScale * recognizer.scale;
if (effectiveScale < 1.0)
effectiveScale = 1.0;
CGFloat maxScaleAndCropFactor = [[stillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor];
if (effectiveScale > maxScaleAndCropFactor)
effectiveScale = maxScaleAndCropFactor;
[CATransaction begin];
[CATransaction setAnimationDuration:.025];
[previewLayer setAffineTransform:CGAffineTransformMakeScale(effectiveScale, effectiveScale)];
[CATransaction commit];
}
}
以上代码的核心是针对预览图层 AVCaptureVideoPreviewLayer
做了放大或者缩小变换,但是这并没有改变拍摄出来的照片是变焦前的图片的本质!我们还要接着往下做工作:
- (void)recordButtonClick:(UIButton *)sender
{
//imgaeOutput 此时没有捕获图像
if (self.imgaeOutput.capturingStillImage == NO)
{
//获取指定连接
AVCaptureConnection *stillImageConnection = [self.imgaeOutput connectionWithMediaType:AVMediaTypeVideo];
//设置视频方向
UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
AVCaptureVideoOrientation avcaptureOrientation = (AVCaptureVideoOrientation)curDeviceOrientation;
if (curDeviceOrientation == UIDeviceOrientationLandscapeLeft){
avcaptureOrientation = AVCaptureVideoOrientationLandscapeRight;
}else if(curDeviceOrientation == UIDeviceOrientationLandscapeRight){
avcaptureOrientation = AVCaptureVideoOrientationLandscapeLeft;
}
[stillImageConnection setVideoOrientation:avcaptureOrientation];
//设置缩放比例
[stillImageConnection setVideoScaleAndCropFactor:effectiveScale];
[self.imgaeOutput setOutputSettings:@{AVVideoCodecKey:AVVideoCodecJPEG}];
[self.imgaeOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
if (error){
}else{
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:jpegData];
dispatch_async(dispatch_get_main_queue(), ^{
[self.navigationController pushViewController:[[AVImageViewController alloc]initWithImage:image] animated:YES];
});
//写入相册
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, imageDataSampleBuffer, kCMAttachmentMode_ShouldPropagate);
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageDataToSavedPhotosAlbum:jpegData metadata:(__bridge id)attachments completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
}
}];
if (attachments)
CFRelease(attachments);
}
}];
}
}
在拍照时,获取当前缩放比例并设置AVCaptureConnection
的缩放比例videoScaleAndCropFactor
//获取指定连接
AVCaptureConnection *stillImageConnection = [self.imgaeOutput connectionWithMediaType:AVMediaTypeVideo];
//设置缩放比例
[stillImageConnection setVideoScaleAndCropFactor:effectiveScale];
点击查看更多AVCaptureDevice信息
点击查看更多AVCaptureConnection信息