The previous answer can be done without the internal methods, allowing it to be more straightforward and understandable.
To fully explain the code:
The zoom
variable keeps track of what zoom you were at after the last gesture. Before any gesture happens there is no zoom, so you're at 1.0.
During a gesture the scale
property of pinch
holds the ratio of the pinch during the active gesture. This is 1.0 when your fingers haven't moved from their initial position and grows and shrinks with pinching. By multiplying this with the previously held zoom
you get what scale to be at in the moment while the gesture is occurring. It's important to keep this scale in the range of [1, device.activeFormat.videoMaxZoomFactor]
or you'll get a SIGABRT
.
When the gesture finishes (pinch.state
) you need to update zoom
so that the next gesture starts at the current zoom level.
It's important to lock when modifying a camera property to avoid concurrent modification. defer
will release the lock after the block of code no matter what, similar to a finally
block.
var zoom: CGFloat = 1.0
@objc func pinch(_ pinch: UIPinchGestureRecognizer) {
guard let device = frontDevice
else { return }
let scaleFactor = min(max(pinch.scale * zoom, 1.0), device.activeFormat.videoMaxZoomFactor)
if pinch.state == .ended {
zoom = scaleFactor
}
do {
try device.lockForConfiguration()
defer { device.unlockForConfiguration() }
device.videoZoomFactor = scaleFactor
} catch {
print(error)
}
}