📸

【Swift】カメラのズーム倍率をアニメーションさせながら操作する

2024/10/04に公開

概要

カメラアプリを作る際、AVFoundationフレームワークを利用します。
弊社が開発しているカメラアプリNow Cameraで、AVCaptureDeviceのズーム機能周りをリファクタする実装に関わったので、備忘録としてまとめました。

ズームの基本

キャプチャーセッションの初期化やinputDevice周りの設定を終えた状態で、AVCaptureDevice.videoZoomFactor: CGFloatに倍率を代入します。

実装

let device: AVCaptureDevice = ...

try device.lockForConfiguration()
device.videoZoomFactor = 4.0 // 👈 ズームさせたい倍率を指定
device.unlockForConfiguration()

ズームをアニメーションさせる

videoZoomFactorに直接値を代入すると、パッと一瞬で倍率が切り替わります。アニメーションさせたい場合は代わりにAVCaptureDevice.ramp(toVideoZoomFactor:withRate:) メソッドでアニメーションさせることができます。
toVideoZoomFactor目標のズーム倍率を、rateはズーム倍率の変化率を指定します。[2]

let device: AVCaptureDevice = ...
let factor: CGFloat = ...

try device.lockForConfiguration()
device.ramp(toVideoZoomFactor: factor, withRate: 2.0) // 👈 アニメーションさせながらズーム倍率を変化させる
device.unlockForConfiguration()

ズームアニメーションをキャンセルする

rampのアニメーションを途中でキャンセルすることもできるようです。AVCaptureDevice.cancelVideoZoomRamp()でキャンセルできます。

let device: AVCaptureDevice = ...

if device.isRampingVideoZoom {
    try device.lockForConfiguration()
    device.cancelVideoZoomRamp() // 👈 ズームアニメーションをキャンセルする
    device.unlockForConfiguration()
}

AVCaptureDeviceのズーム倍率の最小値、最大値を取得する

videoZoomFactorに指定できる値の最小値と最大値は、minAvailableVideoZoomFactor: CGFloatminAvailableVideoZoomFactor: CGFloatに格納されています。

ズーム倍率をUIに表示する

超広角カメラが搭載されているデバイス等で純正カメラアプリを起動すると、ズーム倍率の最小値が0.5と表示されることがあります。しかし、AVCaptureDevicevideoZoomFactorの最小値は1.0です。
どちらも同じズーム倍率を指しているのですが、AVCaptureDevice.displayVideoZoomFactorMultiplier: CGFloatの係数を利用することでUI上いい感じにできます。
ただし、iOS 18.0以降でのみ利用可能なAPIなのでそれ以前のOSでのフォールバックも考慮する必要があります。😢

let device: AVCaptureDevice = ...
let currentZoomFactor: CGFloat = ...

@available(iOS 18.0, *)
var displayingZoomFactor: CGFloat {
    currentZoomFactor * device.displayVideoZoomFactorMultiplier // 👈 倍率をUI上でいい感じにする
}

// Camera View
Text("ズーム倍率は \(displayingZoomFactor) です。")

関連

https://developer.apple.com/documentation/avfoundation/avcapturedevice/zoom

https://ryuta46.com/1347

脚注
  1. AVCaptureDevice Discussion ↩︎

  2. ramp(toVideoZoomFactor:rate:) Disucssion ↩︎

Discussion