アプリでカメラフィードを取得して写真を撮るために、単純な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);
}
}
}
速度にアークタン関数を追加すると、ズームインズームアウト効果が少し緩和されることがわかりました。完全ではありませんが、効果はニーズに十分に合っています。ズームアウトがほぼ1に達したときに、ズームアウトを簡単にする別の機能がある可能性があります。
注:また、ピンチジェスチャのスケールは0から無限になります。0から1はピンチイン(ズームイン)で、1から無限はピンチアウト(ズームイン)です。これで適切なズームインズームアウト効果を得るには、数学の方程式が必要になります。速度は実際には-無限から無限であり、0が開始点です。
編集:範囲例外でのクラッシュを修正しました。@garafajonに感謝します!
多くの人が、レイヤーの変換プロパティを「ここCGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
を
参照」に設定して、ピンチからズームへの本格的な実装を試みました。
videoZoomFactor
iOS 7以降、のプロパティを使用してズームを直接設定できます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
スレッドセーフではないことに注意してください。したがって、メインキューからこれを実行したくない場合があります。
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
迅速なバージョンでは、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);
}
}
}
ピンチレコグナイザーでカメラのズームレベルを処理する簡単な方法があります。あなたがする必要がある唯一のことは、このような状態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);
}
}
}
}
私はiOSSDK8.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];
}
}];