20

我有一个简单的 AVCaptureSession 正在运行,以在我的应用程序中获取相机源并拍照。如何使用UIGestureRecognizer相机实现“捏缩放”功能?

4

9 回答 9

45

接受的答案实际上已经过时,我不确定它是否真的会拍摄放大图像的照片。有一种方法可以像 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

于 2015-07-03T21:37:41.037 回答
6

从 iOS 7 开始,您可以直接videoZoomFactor使用AVCaptureDevice.

将 的scale属性UIPinchGestureRecognizervideoZoomFactor缩放常数联系起来。这将让您改变对味觉的敏感度:

-(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是线程安全的。因此,您可能不想从主队列中执行此操作。

于 2015-04-07T21:39:01.957 回答
6

许多人试图通过将图层上的变换属性设置为“CGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y); 查看此处”来实现此功能,以实现捏合缩放的完整实现。

于 2012-04-19T03:04:26.257 回答
5

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
于 2017-12-24T19:36:58.257 回答
2

我从@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);
        }
    }
}
于 2018-03-25T10:44:38.853 回答
2

在 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

于 2017-01-13T04:33:07.847 回答
1

有一种更简单的方法可以使用捏合识别器处理相机缩放级别。您唯一需要做的就是将其设置为像这样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
    }
}
于 2019-11-05T04:51:46.673 回答
0

基于@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);
            }
        }
    }
}
于 2019-04-16T07:15:30.397 回答
-2

我正在使用 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];
}
}];
于 2015-07-14T06:22:57.940 回答