我有一个简单的 AVCaptureSession 正在运行,以在我的应用程序中获取相机源并拍照。如何使用UIGestureRecognizer
相机实现“捏缩放”功能?
9 回答
接受的答案实际上已经过时,我不确定它是否真的会拍摄放大图像的照片。有一种方法可以像 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 到无限是捏合(放大)。要获得良好的放大缩小效果,您需要有一个数学方程式。速度实际上是从 -infinite 到无穷大,0 是起点。
编辑:修复了范围异常的崩溃。感谢@garafajon!
从 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
是线程安全的。因此,您可能不想从主队列中执行此操作。
许多人试图通过将图层上的变换属性设置为“CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
查看此处”来实现此功能,以实现捏合缩放的完整实现。
Swift 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
我从@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);
}
}
}
在 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
有一种更简单的方法可以使用捏合识别器处理相机缩放级别。您唯一需要做的就是将其设置为像这样cameraDevice.videoZoomFactor
的状态识别器.began
@objc private func viewPinched(recognizer: UIPinchGestureRecognizer) {
switch recognizer.state {
case .began:
recognizer.scale = cameraDevice.videoZoomFactor
case .changed:
let scale = recognizer.scale
do {
try cameraDevice.lockForConfiguration()
cameraDevice.videoZoomFactor = max(cameraDevice.minAvailableVideoZoomFactor, min(scale, cameraDevice.maxAvailableVideoZoomFactor))
cameraDevice.unlockForConfiguration()
}
catch {
print(error)
}
default:
break
}
}
基于@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];
}
}];