捏合缩放相机

18

我正在尝试制作一个捏合缩放相机,但遇到了两个问题。首先,它允许用户过度缩小和过度放大,其次,当我拍照时,它不会将其拍摄为缩放视图。以下是我的捏合功能代码...

func pinch(pinch: UIPinchGestureRecognizer) {
    if let view = cameraView {
        view.transform = CGAffineTransformScale(view.transform,
            pinch.scale, pinch.scale)
            pinch.scale = 1
    }

}

如果需要查看更多的代码,请告诉我。谢谢!

7个回答

51

Swift 3.0 || 4.0


1. 定义缩放级别。

let minimumZoom: CGFloat = 1.0
let maximumZoom: CGFloat = 3.0
var lastZoomFactor: CGFloat = 1.0


2. 在 CameraView 上添加捏合手势。

let pinchRecognizer = UIPinchGestureRecognizer(target: self, action:#selector(pinch(_:)))        
            self.viewCamera.addGestureRecognizer(pinchRecognizer)


3. 通过“缩小”和“放大”的逻辑,使用捏合操作方法。

func pinch(_ pinch: UIPinchGestureRecognizer) {
        guard let device = videoDeviceInput.device else { return }

        // Return zoom value between the minimum and maximum zoom values
        func minMaxZoom(_ factor: CGFloat) -> CGFloat {
            return min(min(max(factor, minimumZoom), maximumZoom), device.activeFormat.videoMaxZoomFactor)
        }

        func update(scale factor: CGFloat) {
            do {
                try device.lockForConfiguration()
                defer { device.unlockForConfiguration() }
                device.videoZoomFactor = factor
            } catch {
                print("\(error.localizedDescription)")
            }
        }

        let newScaleFactor = minMaxZoom(pinch.scale * lastZoomFactor)

        switch pinch.state {
        case .began: fallthrough
        case .changed: update(scale: newScaleFactor)
        case .ended:
            lastZoomFactor = minMaxZoom(newScaleFactor)
            update(scale: lastZoomFactor)
        default: break
        }
    }


谢谢。愉快的编码


1
非常好而且写得很好的回答。谢谢! - stonecompass
谢谢,很好的例子。 - LoVo

16

我也遇到了相机实现的相同问题。要解决此问题,您需要知道两件事:

  • 最大缩放倍数和最小缩放倍数必须在一个范围内,否则会导致相机过度放大。
  • 关于实际图像未保存缩放后的图像,这是很多在线解决方案没有涉及的常见错误。实际上,这是因为您只更改了视图的缩放而没有更改实际的 AVCaptureDevice 的缩放。

要更改这两个内容,您需要像这样:

func pinch(pinch: UIPinchGestureRecognizer) {
   var device: AVCaptureDevice = self.videoDevice
   var vZoomFactor = ((gestureRecognizer as! UIPinchGestureRecognizer).scale)
   var error:NSError!
        do{
            try device.lockForConfiguration()
            defer {device.unlockForConfiguration()}
            if (vZoomFactor <= device.activeFormat.videoMaxZoomFactor){
                device.videoZoomFactor = vZoomFactor
            }else{
            NSLog("Unable to set videoZoom: (max %f, asked %f)", device.activeFormat.videoMaxZoomFactor, vZoomFactor);
            }
        }catch error as NSError{
             NSLog("Unable to set videoZoom: %@", error.localizedDescription);
        }catch _{

        }
}

正如您所看到的,我使用一个类变量来表示视频设备 (videoDevice) ,以跟踪我正在用于视觉组件的捕获设备。我将变焦限制在特定范围内,并更改设备上的变焦属性而不是视图本身!


3
有一件事情需要补充,您需要跟踪当前缩放比例,并在计算应设置的缩放因子时使用它。但是感谢您的出色回答! - stonecompass
你好,当我尝试缩小时,你的答案对我来说崩溃了。你能告诉我我做错了什么吗? - Karthik Kannan

7

您可以通过将UIPinchGestureRecognizer.scale重置为1来避免保存prevZoomFactor,示例如下:

    @IBAction func pinchAction(_ sender: UIPinchGestureRecognizer) {
        guard let device = currentCaptureDevice else {return}
        var zoom = device.videoZoomFactor * sender.scale
        sender.scale = 1.0
        var error:NSError!
        do{
            try device.lockForConfiguration()
            defer {device.unlockForConfiguration()}
            if zoom >= device.minAvailableVideoZoomFactor && zoom <= device.maxAvailableVideoZoomFactor {
                device.videoZoomFactor = zoom
            }else{
                NSLog("Unable to set videoZoom: (max %f, asked %f)", device.activeFormat.videoMaxZoomFactor, zoom);
            }
        }catch error as NSError{
            NSLog("Unable to set videoZoom: %@", error.localizedDescription);
        }catch _{
        }
    }

我曾参加过一次苹果公司WWDC活动,他们推荐使用手势识别器。

2
如果您需要手动zoomTo(2.0)函数,您可以使用以下代码:
// Create listener for Pinch to Zoom
let pinchRecognizer = UIPinchGestureRecognizer(target: self, action:#selector(FSCameraView.pinchToZoom(_:)))
pinchRecognizer.delegate = self
self.previewViewContainer.addGestureRecognizer(pinchRecognizer)

// set the zoom to a zoomed in mode from start
setZoom(CGFloat(2.0)



// and the functions
func pinchToZoom(sender:UIPinchGestureRecognizer) {
    var vZoomFactor = ((sender as! UIPinchGestureRecognizer).scale)
    setZoom(vZoomFactor)
}

func setZoom(zoomFactor:CGFloat) {
    var device: AVCaptureDevice = self.device!
    var error:NSError!
    do{
        try device.lockForConfiguration()
        defer {device.unlockForConfiguration()}
        if (zoomFactor <= device.activeFormat.videoMaxZoomFactor) {

            let desiredZoomFactor:CGFloat = zoomFactor + atan2(sender.velocity, 5.0);
            device.videoZoomFactor = max(1.0, min(desiredZoomFactor, device.activeFormat.videoMaxZoomFactor));
        }
        else {
            NSLog("Unable to set videoZoom: (max %f, asked %f)", device.activeFormat.videoMaxZoomFactor, zoomFactor);
        }
    }
    catch error as NSError{
        NSLog("Unable to set videoZoom: %@", error.localizedDescription);
    }
    catch _{
    }
}

1
    var device: AVCaptureDevice = self.backCamera
    var vZoomFactor = sender.scale
    var error:NSError!
    do{
        try device.lockForConfiguration()
        defer {device.unlockForConfiguration()}
        if (vZoomFactor <= device.activeFormat.videoMaxZoomFactor) {

            let desiredZoomFactor:CGFloat = vZoomFactor + atan2(sender.velocity, 5.0);
            device.videoZoomFactor = max(1.0, min(desiredZoomFactor, device.activeFormat.videoMaxZoomFactor));
        }
        else {

            NSLog("Unable to set videoZoom: (max %f, asked %f)", device.activeFormat.videoMaxZoomFactor, vZoomFactor);
        }
    }
    catch error as NSError{

        NSLog("Unable to set videoZoom: %@", error.localizedDescription);
    }
    catch _{

    }

1

Swift 5.0或以上版本

1. 定义变量声明和输出口。

//#MARK: - Outlets
@IBOutlet weak var camPreview: UIView!

let minimumZoom: CGFloat = 1.0
let maximumZoom: CGFloat = 2.0
var lastZoomFactor: CGFloat = 1.0
var newCamera: AVCaptureDevice?

2. 获取相机视图上的相机位置和捏合手势。

//#MARK: - Views methods    
  override func viewDidLoad() {
     super.viewDidLoad()
   // Do any additional setup after loading the view.
    newCamera = cameraWithPosition(position: .back)  
    //Add Pinch Gesture on CameraView.
      let pinchRecognizer = UIPinchGestureRecognizer(target: self, action:#selector(pinch(_:)))
       self.camPreview.addGestureRecognizer(pinchRecognizer)
  }

// Find a camera with the specified AVCaptureDevicePosition, returning nil if one is not found

  func cameraWithPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
      let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified)
      for device in discoverySession.devices {
            if device.position == position {
                return device
            }
       }
       return nil
  }

3. 带有缩放和放大功能的捏合动作。

  @objc func pinch(_ pinch: UIPinchGestureRecognizer) {
          guard let device = newCamera else { return }
    
          // Return zoom value between the minimum and maximum zoom values
          func minMaxZoom(_ factor: CGFloat) -> CGFloat {
                return min(min(max(factor, minimumZoom), maximumZoom), device.activeFormat.videoMaxZoomFactor)
          }
    
          func update(scale factor: CGFloat) {
               do {
                     try device.lockForConfiguration()
                     defer { device.unlockForConfiguration() }
                     device.videoZoomFactor = factor
                  } catch {
                     print("\(error.localizedDescription)")
                  }
          }
    
          let newScaleFactor = minMaxZoom(pinch.scale * lastZoomFactor)
    
          switch pinch.state {
                case .began: fallthrough
                case .changed: update(scale: newScaleFactor)
                case .ended:
                    lastZoomFactor = minMaxZoom(newScaleFactor)
                    update(scale: lastZoomFactor)
                default: break
                }
           }
  }

1
为了补充Ritvik Upadhyaya的回答,您还需要保存先前的缩放因子以计算新的缩放因子,否则每次抬起手指再尝试缩放时都会重置缩放。
// To track the zoom factor
var prevZoomFactor: CGFloat = 1

func pinch(pinch: UIPinchGestureRecognizer) {
    var device: AVCaptureDevice = self.videoDevice

    // Here we multiply vZoomFactor with the previous zoom factor if it exist. 
    // Else just multiply by 1
    var vZoomFactor = pinch.scale * prevZoomFactor

    // If the pinching has ended, update prevZoomFactor.
    // Note that we set the limit at 1, because zoom factor cannot be less than 1 or the setting device.videoZoomFactor will crash
    if sender.state == .ended {
        prevZoomFactor = zoomFactor >= 1 ? zoomFactor : 1
    }

    do {
        try device.lockForConfiguration()
        defer {device.unlockForConfiguration()}
        if (vZoomFactor <= device.activeFormat.videoMaxZoomFactor) {
            device.videoZoomFactor = vZoomFactor
        } else {
            print("Unable to set videoZoom: (max \(device.activeFormat.videoMaxZoomFactor), asked \(vZoomFactor))")
      }
    } catch {
        print("\(error.localizedDescription)")
    }
}

1
你好。如果vZoomFactor小于1.0,应用程序会崩溃吗? - El Tomato

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接