Reputation:
I am trying to replicate Snapchat camera's zoom feature where once you have started recording you can drag your finger up or down and it will zoom in or out accordingly. I have been successful with zooming on pinch but have been stuck on zooming with the PanGestureRecognizer
.
Here is the code I've tried the problem is that I do not know how to replace the sender.scale
that I use for pinch gesture recognizer zooming. I'm using AVFoundation. Basically, I'm asking how I can do the hold zoom (one finger drag) like in TikTok or Snapchat properly.
let minimumZoom: CGFloat = 1.0
let maximumZoom: CGFloat = 15.0
var lastZoomFactor: CGFloat = 1.0
var latestDirection: Int = 0
@objc func panGesture(_ sender: UIPanGestureRecognizer) {
let velocity = sender.velocity(in: doubleTapSwitchCamButton)
var currentDirection: Int = 0
if velocity.y > 0 || velocity.y < 0 {
let originalCapSession = captureSession
var devitce : AVCaptureDevice!
let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDuoCamera], mediaType: AVMediaType.video, position: .unspecified)
let devices = videoDeviceDiscoverySession.devices
devitce = devices.first!
guard let device = devitce 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)")
}
}
//These 2 lines below are the problematic ones, pinch zoom uses this one below, and the newScaleFactor below that is a testing one that did not work.
let newScaleFactor = minMaxZoom(sender.scale * lastZoomFactor)
//let newScaleFactor = CGFloat(exactly: number + lastZoomFactor)
switch sender.state {
case .began: fallthrough
case .changed: update(scale: newScaleFactor!)
case .ended:
lastZoomFactor = minMaxZoom(newScaleFactor!)
update(scale: lastZoomFactor)
default: break
}
} else {
}
latestDirection = currentDirection
}
Upvotes: 1
Views: 1308
Reputation: 474
You can use the gesture recogniser's translation property, to calculate the displacement in points, and normalise this displacement as a zoom factor.
Fitting this into your code, you could try:
... somewhere in your view setup code, i.e. viewDidLoad....
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGesture))
button.addGestureRecognizer(panGestureRecognizer)
private var initialZoom: CGFloat = 1.0
@objc func panGesture(_ sender: UIPanGestureRecognizer) {
// note that 'view' here is the overall video preview
let velocity = sender.velocity(in: view)
if velocity.y > 0 || velocity.y < 0 {
let originalCapSession = captureSession
var devitce : AVCaptureDevice!
let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInDuoCamera], mediaType: AVMediaType.video, position: .unspecified)
let devices = videoDeviceDiscoverySession.devices
devitce = devices.first!
guard let device = devitce else { return }
let minimumZoomFactor: CGFloat = 1.0
let maximumZoomFactor: CGFloat = min(device.activeFormat.videoMaxZoomFactor, 10.0) // artificially set a max useable zoom of 10x
// clamp a zoom factor between minimumZoom and maximumZoom
func clampZoomFactor(_ factor: CGFloat) -> CGFloat {
return min(max(factor, minimumZoomFactor), maximumZoomFactor)
}
func update(scale factor: CGFloat) {
do {
try device.lockForConfiguration()
defer { device.unlockForConfiguration() }
device.videoZoomFactor = factor
} catch {
print("\(error.localizedDescription)")
}
}
switch sender.state {
case .began:
initialZoom = device.videoZoomFactor
startRecording() /// call to start recording your video
case .changed:
// distance in points for the full zoom range (e.g. min to max), could be view.frame.height
let fullRangeDistancePoints: CGFloat = 300.0
// extract current distance travelled, from gesture start
let currentYTranslation: CGFloat = sender.translation(in: view).y
// calculate a normalized zoom factor between [-1,1], where up is positive (ie zooming in)
let normalizedZoomFactor = -1 * max(-1,min(1,currentYTranslation / fullRangeDistancePoints))
// calculate effective zoom scale to use
let newZoomFactor = clampZoomFactor(initialZoom + normalizedZoomFactor * (maximumZoomFactor - minimumZoomFactor))
// update device's zoom factor'
update(scale: newZoomFactor)
case .ended, .cancelled:
stopRecording() /// call to start recording your video
break
default:
break
}
}
}
Upvotes: 4