我有一个简单的AVCaptureSession运行,以便在我的应用程序中获取相机并拍照。如何使用UIGestureRecognizer
为相机实现“捏缩放”功能?
接受的答案实际上是过时的,我不确定它实际上会拍摄放大图像的照片。有一种放大方法,如bcattle回答说。他回答的问题是它没有掌握用户可以放大然后从该变焦位置重新启动的事实。他的解决方案会创造一些不太优雅的跳跃。
最简单和最优雅的方法是使用捏手势的速度。
-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
const CGFloat pinchVelocityDividerFactor = 5.0f;
if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSError *error = nil;
if ([videoDevice lockForConfiguration:&error]) {
CGFloat desiredZoomFactor = device.videoZoomFactor + atan2f(pinchRecognizer.velocity, pinchVelocityDividerFactor);
// Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
device.videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, device.activeFormat.videoMaxZoomFactor));
[videoDevice unlockForConfiguration];
} else {
NSLog(@"error: %@", error);
}
}
}
我发现将arctan函数添加到速度将有助于放大缩小效果。它并不完美,但效果足以满足需求。当它几乎达到1时,可能还有另一个功能可以轻松缩小。
注意:此外,捏合手势的比例从0变为无穷大,0到1被捏入(缩小),1到无限被捏出(放大)。要想有一个很好的放大缩小效果,你需要有一个数学方程式。速度实际上是从无限到无穷,0是起点。
编辑:修复范围异常崩溃。感谢@garafajon!
许多人试图通过将图层上的transform属性设置为CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
来实现这一点。请参阅here以获得完全成熟的缩放缩放实现。
从iOS 7开始,您可以使用videoZoomFactor
的AVCaptureDevice
属性直接设置缩放。
使用缩放常数将scale
的UIPinchGestureRecognizer
属性绑定到videoZoomFactor
。这将让您改变对口味的敏感度:
-(void) handlePinchToZoomRecognizer:(UIPinchGestureRecognizer*)pinchRecognizer {
const CGFloat pinchZoomScaleFactor = 2.0;
if (pinchRecognizer.state == UIGestureRecognizerStateChanged) {
NSError *error = nil;
if ([videoDevice lockForConfiguration:&error]) {
videoDevice.videoZoomFactor = 1.0 + pinchRecognizer.scale * pinchZoomScaleFactor;
[videoDevice unlockForConfiguration];
} else {
NSLog(@"error: %@", error);
}
}
}
请注意,AVCaptureDevice
以及与AVCaptureSession
相关的所有其他内容都不是线程安全的。所以你可能不希望从主队列中这样做。
斯威夫特4 将捏合手势识别器添加到最前面的视图并将其连接到此操作(pinchToZoom)。 captureDevice应该是当前为捕获会话提供输入的实例。 pinchToZoom为前后捕获设备提供平滑的缩放。
@IBAction func pinchToZoom(_ pinch: UIPinchGestureRecognizer) {
guard let device = captureDevice else { return }
func minMaxZoom(_ factor: CGFloat) -> CGFloat { return min(max(factor, 1.0), device.activeFormat.videoMaxZoomFactor) }
func update(scale factor: CGFloat) {
do {
try device.lockForConfiguration()
defer { device.unlockForConfiguration() }
device.videoZoomFactor = factor
} catch {
debugPrint(error)
}
}
let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
switch sender.state {
case .began: fallthrough
case .changed: update(scale: newScaleFactor)
case .ended:
zoomFactor = minMaxZoom(newScaleFactor)
update(scale: zoomFactor)
default: break
}
}
在相机或vc上声明zoomFactor会很有用。我通常将它放在具有AVCaptureSession的同一单身人士身上。这将作为captureDevice的videoZoomFactor的默认值。
var zoomFactor: Float = 1.0
在swift版本中,您可以通过在videoZoomFactor上传递缩放的数字来放大/缩小。 UIPinchGestureRecognizer处理程序中的以下代码将解决该问题。
do {
try device.lockForConfiguration()
switch gesture.state {
case .began:
self.pivotPinchScale = device.videoZoomFactor
case .changed:
var factor = self.pivotPinchScale * gesture.scale
factor = max(1, min(factor, device.activeFormat.videoMaxZoomFactor))
device.videoZoomFactor = factor
default:
break
}
device.unlockForConfiguration()
} catch {
// handle exception
}
在这里,pivotPinchScale是一个CGFloat属性,在你的控制器某处声明。
您还可以参考以下项目,了解相机如何与UIPinchGestureRecognizer配合使用。 https://github.com/DragonCherry/CameraPreviewController
我从@Gabriel Cartier的解决方案开始(谢谢)。在我的代码中,我更喜欢使用更平滑的rampToVideoZoomFactor和更简单的方法来计算设备的比例因子。
(IBAction) pinchForZoom:(id) sender forEvent:(UIEvent*) event {
UIPinchGestureRecognizer* pinchRecognizer = (UIPinchGestureRecognizer *)sender;
static CGFloat zoomFactorBegin = .0;
if ( UIGestureRecognizerStateBegan == pinchRecognizer.state ) {
zoomFactorBegin = self.captureDevice.videoZoomFactor;
} else if (UIGestureRecognizerStateChanged == pinchRecognizer.state) {
NSError *error = nil;
if ([self.captureDevice lockForConfiguration:&error]) {
CGFloat desiredZoomFactor = zoomFactorBegin * pinchRecognizer.scale;
CGFloat zoomFactor = MAX(1.0, MIN(desiredZoomFactor, self.captureDevice.activeFormat.videoMaxZoomFactor));
[self.captureDevice rampToVideoZoomFactor:zoomFactor withRate:3.0];
[self.captureDevice unlockForConfiguration];
} else {
NSLog(@"error: %@", error);
}
}
}
根据@Gabriel Cartier的回答:
- (void) cameraZoomWithPinchVelocity: (CGFloat)velocity {
CGFloat pinchVelocityDividerFactor = 40.0f;
if (velocity < 0) {
pinchVelocityDividerFactor = 5.; //zoom in
}
if (_videoInput) {
if([[_videoInput device] position] == AVCaptureDevicePositionBack) {
NSError *error = nil;
if ([[_videoInput device] lockForConfiguration:&error]) {
CGFloat desiredZoomFactor = [_videoInput device].videoZoomFactor + atan2f(velocity, pinchVelocityDividerFactor);
// Check if desiredZoomFactor fits required range from 1.0 to activeFormat.videoMaxZoomFactor
CGFloat maxFactor = MIN(10, [_videoInput device].activeFormat.videoMaxZoomFactor);
[_videoInput device].videoZoomFactor = MAX(1.0, MIN(desiredZoomFactor, maxFactor));
[[_videoInput device] unlockForConfiguration];
} else {
NSLog(@"cameraZoomWithPinchVelocity error: %@", error);
}
}
}
}
我正在使用iOS SDK 8.3和AVfoundation框架,并使用以下方法为我工作:
nameOfAVCaptureVideoPreviewLayer.affineTransform = CGAffineTransformMakeScale(scaleX, scaleY)
为了以相同的比例保存图片,我使用了以下方法:
nameOfAVCaptureConnection.videoScaleAndCropFactor = factorNumber;
下面的代码用于获取刻度中的图像
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if(imageDataSampleBuffer != NULL){
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
}
}];