Skip to content

Commit 60dbad4

Browse files
authored
Merge pull request #204 from guischulz/tap-focus-pinch-zoom-ios
Add tap to focus and pinch to zoom for iOS
2 parents c284306 + f6aff05 commit 60dbad4

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Starts the camera preview instance.
8181
| disableAudio | boolean | (optional) Disables audio stream to prevent permission requests, default false. (applicable to web only) |
8282
| lockAndroidOrientation | boolean | (optional) Locks device orientation when camera is showing, default false. (applicable to Android only) |
8383
| enableOpacity | boolean | (optional) Make the camera preview see-through. Ideal for augmented reality uses. Default false (applicable to Android and web only)
84-
| enableZoom | boolean | (optional) Set if you can pinch to zoom. Default false (applicable to Android only)
84+
| enableZoom | boolean | (optional) Set if you can pinch to zoom. Default false (applicable to the android and ios platforms only)
8585

8686
<!-- <strong>Options:</strong>
8787
All options stated are optional and will default to values here

ios/Plugin/CameraController.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class CameraController: NSObject {
3434

3535
var audioDevice: AVCaptureDevice?
3636
var audioInput: AVCaptureDeviceInput?
37+
38+
var zoomFactor: CGFloat = 1.0
3739
}
3840

3941
extension CameraController {
@@ -162,6 +164,25 @@ extension CameraController {
162164
self.previewLayer?.frame = view.frame
163165
}
164166

167+
func setupGestures(target: UIView, enableZoom: Bool) {
168+
setupTapGesture(target: target, selector: #selector(handleTap(_:)), delegate: self)
169+
if (enableZoom) {
170+
setupPinchGesture(target: target, selector: #selector(handlePinch(_:)), delegate: self)
171+
}
172+
}
173+
174+
func setupTapGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
175+
let tapGesture = UITapGestureRecognizer(target: self, action: selector)
176+
tapGesture.delegate = delegate
177+
target.addGestureRecognizer(tapGesture)
178+
}
179+
180+
func setupPinchGesture(target: UIView, selector: Selector, delegate: UIGestureRecognizerDelegate?) {
181+
let pinchGesture = UIPinchGestureRecognizer(target: self, action: selector)
182+
pinchGesture.delegate = delegate
183+
target.addGestureRecognizer(pinchGesture)
184+
}
185+
165186
func updateVideoOrientation() {
166187
assert(Thread.isMainThread) // UIApplication.statusBarOrientation requires the main thread.
167188

@@ -416,6 +437,67 @@ extension CameraController {
416437
}
417438
}
418439

440+
extension CameraController: UIGestureRecognizerDelegate {
441+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
442+
return true;
443+
}
444+
445+
@objc
446+
func handleTap(_ tap: UITapGestureRecognizer) {
447+
guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
448+
449+
let point = tap.location(in: tap.view)
450+
let devicePoint = self.previewLayer?.captureDevicePointConverted(fromLayerPoint: point)
451+
452+
do {
453+
try device.lockForConfiguration()
454+
defer { device.unlockForConfiguration() }
455+
456+
let focusMode = AVCaptureDevice.FocusMode.autoFocus
457+
if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(focusMode) {
458+
device.focusPointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
459+
device.focusMode = focusMode
460+
}
461+
462+
let exposureMode = AVCaptureDevice.ExposureMode.autoExpose
463+
if device.isExposurePointOfInterestSupported && device.isExposureModeSupported(exposureMode) {
464+
device.exposurePointOfInterest = CGPoint(x: CGFloat(devicePoint?.x ?? 0), y: CGFloat(devicePoint?.y ?? 0))
465+
device.exposureMode = exposureMode
466+
}
467+
} catch {
468+
debugPrint(error)
469+
}
470+
}
471+
472+
@objc
473+
private func handlePinch(_ pinch: UIPinchGestureRecognizer) {
474+
guard let device = self.currentCameraPosition == .rear ? rearCamera : frontCamera else { return }
475+
476+
func minMaxZoom(_ factor: CGFloat) -> CGFloat { return max(1.0, min(factor, device.activeFormat.videoMaxZoomFactor)) }
477+
478+
func update(scale factor: CGFloat) {
479+
do {
480+
try device.lockForConfiguration()
481+
defer { device.unlockForConfiguration() }
482+
483+
device.videoZoomFactor = factor
484+
} catch {
485+
debugPrint(error)
486+
}
487+
}
488+
489+
switch pinch.state {
490+
case .began: fallthrough
491+
case .changed:
492+
let newScaleFactor = minMaxZoom(pinch.scale * zoomFactor)
493+
update(scale: newScaleFactor)
494+
case .ended:
495+
zoomFactor = device.videoZoomFactor
496+
default: break
497+
}
498+
}
499+
}
500+
419501
extension CameraController: AVCapturePhotoCaptureDelegate {
420502
public func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
421503
resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {

ios/Plugin/Plugin.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class CameraPreview: CAPPlugin {
1919
var rotateWhenOrientationChanged: Bool?
2020
var toBack: Bool?
2121
var storeToFile: Bool?
22+
var enableZoom: Bool?
2223
var highResolutionOutput: Bool = false
2324

2425
@objc func rotated() {
@@ -78,6 +79,7 @@ public class CameraPreview: CAPPlugin {
7879
self.rotateWhenOrientationChanged = call.getBool("rotateWhenOrientationChanged") ?? true
7980
self.toBack = call.getBool("toBack") ?? false
8081
self.storeToFile = call.getBool("storeToFile") ?? false
82+
self.enableZoom = call.getBool("enableZoom") ?? false
8183

8284
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
8385
guard granted else {
@@ -106,6 +108,9 @@ public class CameraPreview: CAPPlugin {
106108
}
107109
try? self.cameraController.displayPreview(on: self.previewView)
108110

111+
let frontView = self.toBack! ? self.webView : self.previewView;
112+
self.cameraController.setupGestures(target: frontView ?? self.previewView, enableZoom: self.enableZoom!)
113+
109114
if (self.rotateWhenOrientationChanged == true) {
110115
NotificationCenter.default.addObserver(self, selector: #selector(CameraPreview.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
111116
}

0 commit comments

Comments
 (0)