我們在自定義相機時,若要實現鏡頭變焦,也就是推近或者拉遠焦距,有兩種方法可以實現:可以通過修改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];