アプリでカメラフィードを取得して写真を撮るために、単純な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に達したときに、ズームアウトを簡単にする別の機能がある可能性があります。
[〜#〜] note [〜#〜]:また、ピンチジェスチャのスケールは0から無限になり、0から1がピンチインします。 (ズームアウト)および1から無限にピンチアウト(ズームイン)。これで適切なズームインズームアウト効果を得るには、数式が必要です。速度は実際には-無限から無限であり、0が開始点です。
[〜#〜] edit [〜#〜]:範囲例外でのクラッシュを修正しました。ありがとう @ garafajon !
多くの人が、レイヤーの変換プロパティをCGAffineTransformMakeScale(gesture.scale.x, gesture.scale.y);
に設定することでこれを行おうとしました。ピンチツーズームの本格的な実装については、 ここ を参照してください。
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
に関連する他のすべてのものと同様に、スレッドセーフではないことに注意してください。したがって、メインキューからこれを実行することはおそらく望ましくありません。
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
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);
}
}
}
}
ピンチレコグナイザーでカメラのズームレベルを処理する簡単な方法があります。あなたがする必要がある唯一のことは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
}
}