From 2a582f0ac0feda5a6067757e576453087a12feee Mon Sep 17 00:00:00 2001 From: Shabbir Vijapura Date: Tue, 16 Jul 2024 12:38:11 -0400 Subject: [PATCH 1/5] Allow saving video to custom path --- Sources/CameraManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CameraManager.swift b/Sources/CameraManager.swift index a568cea..1c61a09 100644 --- a/Sources/CameraManager.swift +++ b/Sources/CameraManager.swift @@ -740,7 +740,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest /** Starts recording a video with or without voice as in the session preset. */ - open func startRecordingVideo() { + open func startRecordingVideo(toURL url: URL? = nil) { guard cameraOutputMode != .stillImage else { _show(NSLocalizedString("Capture session output still image", comment: ""), message: NSLocalizedString("I can only take pictures", comment: "")) return @@ -772,7 +772,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest _updateIlluminationMode(flashMode) - videoOutput.startRecording(to: _tempFilePath(), recordingDelegate: self) + videoOutput.startRecording(to: url ?? _tempFilePath(), recordingDelegate: self) } /** From f96e2cb946c03cefbee22ff38ccca0975ccb3cbe Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Feb 2026 05:49:42 +0000 Subject: [PATCH 2/5] Add support for 0.5x ultra-wide zoom level Changed minimum zoom level from 1.0x to 0.5x to support newer cameras with ultra-wide lenses. Updated documentation to reflect the new zoom range (0.5x to device maximum). https://claude.ai/code/session_01KpFgCpFkFy6d6S2V73DQqm --- README.md | 9 ++++++++- Sources/CameraManager.swift | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62ffd04..229c911 100755 --- a/README.md +++ b/README.md @@ -88,13 +88,20 @@ cameraManager.stopVideoRecording({ (videoURL, recordError) -> Void in }) ``` -To zoom in manually: +To zoom manually: ```swift +// Zoom in let zoomScale = CGFloat(2.0) cameraManager.zoom(zoomScale) + +// Zoom out (ultra-wide, 0.5x - supported on newer cameras) +let zoomScale = CGFloat(0.5) +cameraManager.zoom(zoomScale) ``` +The zoom range is 0.5x to the device's maximum zoom factor. Ultra-wide zoom (0.5x) is available on newer camera devices that support it. + ### Properties You can set input device to front or back camera. `(Default: .Back)` diff --git a/Sources/CameraManager.swift b/Sources/CameraManager.swift index 1c61a09..b50ac81 100644 --- a/Sources/CameraManager.swift +++ b/Sources/CameraManager.swift @@ -971,7 +971,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest let captureDevice = device try captureDevice?.lockForConfiguration() - zoomScale = max(1.0, min(beginZoomScale * scale, maxZoomScale)) + zoomScale = max(0.5, min(beginZoomScale * scale, maxZoomScale)) captureDevice?.videoZoomFactor = zoomScale From fc7b5af8f0f271ad55604329b4bd324bb82c8387 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Feb 2026 06:00:49 +0000 Subject: [PATCH 3/5] Use device-specific minimum zoom instead of hardcoded value Changed zoom implementation to dynamically detect minimum zoom level using minAvailableVideoZoomFactor from the camera device. This ensures ultra-wide cameras are properly supported only on devices that have them. - Added minZoomScale property to track device minimum zoom - Updated _setupMaxZoomScale() to set both min and max zoom limits - Changed _zoom() to use dynamic minZoomScale instead of hardcoded 0.5 - Updated README to clarify that zoom range is device-dependent On multi-camera devices (iPhone 11+), the minimum zoom factor is 1.0, which automatically uses the ultra-wide lens. This is the correct iOS behavior rather than using an arbitrary 0.5x value. https://claude.ai/code/session_01KpFgCpFkFy6d6S2V73DQqm --- README.md | 6 +++--- Sources/CameraManager.swift | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 229c911..eb7c131 100755 --- a/README.md +++ b/README.md @@ -95,12 +95,12 @@ To zoom manually: let zoomScale = CGFloat(2.0) cameraManager.zoom(zoomScale) -// Zoom out (ultra-wide, 0.5x - supported on newer cameras) -let zoomScale = CGFloat(0.5) +// Zoom out (uses ultra-wide camera on supported devices) +let zoomScale = CGFloat(1.0) cameraManager.zoom(zoomScale) ``` -The zoom range is 0.5x to the device's maximum zoom factor. Ultra-wide zoom (0.5x) is available on newer camera devices that support it. +The zoom range is automatically determined by the device's camera capabilities using `minAvailableVideoZoomFactor` and `maxAvailableVideoZoomFactor`. On devices with ultra-wide cameras (iPhone 11+), setting zoom to 1.0 will use the ultra-wide lens. ### Properties diff --git a/Sources/CameraManager.swift b/Sources/CameraManager.swift index b50ac81..26a18bf 100644 --- a/Sources/CameraManager.swift +++ b/Sources/CameraManager.swift @@ -366,6 +366,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest fileprivate var zoomScale = CGFloat(1.0) fileprivate var beginZoomScale = CGFloat(1.0) fileprivate var maxZoomScale = CGFloat(1.0) + fileprivate var minZoomScale = CGFloat(1.0) fileprivate func _tempFilePath() -> URL { let tempURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("tempMovie\(Date().timeIntervalSince1970)").appendingPathExtension("mp4") @@ -971,7 +972,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest let captureDevice = device try captureDevice?.lockForConfiguration() - zoomScale = max(0.5, min(beginZoomScale * scale, maxZoomScale)) + zoomScale = max(minZoomScale, min(beginZoomScale * scale, maxZoomScale)) captureDevice?.videoZoomFactor = zoomScale @@ -1541,15 +1542,19 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest fileprivate func _setupMaxZoomScale() { var maxZoom = CGFloat(1.0) + var minZoom = CGFloat(1.0) beginZoomScale = CGFloat(1.0) - + if cameraDevice == .back, let backCameraDevice = backCameraDevice { maxZoom = backCameraDevice.activeFormat.videoMaxZoomFactor + minZoom = backCameraDevice.minAvailableVideoZoomFactor } else if cameraDevice == .front, let frontCameraDevice = frontCameraDevice { maxZoom = frontCameraDevice.activeFormat.videoMaxZoomFactor + minZoom = frontCameraDevice.minAvailableVideoZoomFactor } - + maxZoomScale = maxZoom + minZoomScale = minZoom } fileprivate func _checkIfCameraIsAvailable() -> CameraState { From 013ffb0498506eb282e9b384e138905ad9f6c6fb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Feb 2026 03:44:34 +0000 Subject: [PATCH 4/5] Fix ultra-wide camera support by using multi-camera virtual devices The previous implementation only selected the first back camera device, which would return the basic wide-angle camera without ultra-wide support. Changed camera discovery to use AVCaptureDevice.DiscoverySession with priority order: 1. .builtInTripleCamera (iPhone 11 Pro+, supports ultra-wide) 2. .builtInDualWideCamera (iPhone 11/13/14, supports ultra-wide) 3. .builtInDualCamera (older dual camera models) 4. .builtInWideAngleCamera (fallback for single camera devices) This ensures that on devices with ultra-wide cameras (iPhone 11+), the multi-camera virtual device is selected, which allows iOS to automatically switch to the ultra-wide lens when zoom is set to 1.0. Updated README to explain which device types are now supported and how ultra-wide camera switching works. https://claude.ai/code/session_01KpFgCpFkFy6d6S2V73DQqm --- README.md | 9 ++++++++- Sources/CameraManager.swift | 21 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb7c131..53afac5 100755 --- a/README.md +++ b/README.md @@ -100,7 +100,14 @@ let zoomScale = CGFloat(1.0) cameraManager.zoom(zoomScale) ``` -The zoom range is automatically determined by the device's camera capabilities using `minAvailableVideoZoomFactor` and `maxAvailableVideoZoomFactor`. On devices with ultra-wide cameras (iPhone 11+), setting zoom to 1.0 will use the ultra-wide lens. +The zoom range is automatically determined by the device's camera capabilities using `minAvailableVideoZoomFactor` and `maxAvailableVideoZoomFactor`. + +**Ultra-Wide Camera Support:** +The library automatically detects and uses multi-camera virtual devices (iPhone 11+) that include ultra-wide lenses: +- iPhone 11 Pro/12 Pro/13 Pro/14 Pro: `.builtInTripleCamera` (ultra-wide + wide + telephoto) +- iPhone 11/13/14: `.builtInDualWideCamera` (ultra-wide + wide) + +On these devices, setting zoom to 1.0 automatically uses the ultra-wide lens. iOS handles the camera switching seamlessly as you zoom in and out. ### Properties diff --git a/Sources/CameraManager.swift b/Sources/CameraManager.swift index 26a18bf..0c50b12 100644 --- a/Sources/CameraManager.swift +++ b/Sources/CameraManager.swift @@ -348,7 +348,26 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest }() fileprivate lazy var backCameraDevice: AVCaptureDevice? = { - AVCaptureDevice.videoDevices.filter { $0.position == .back }.first + // Try to get multi-camera virtual devices first (supports ultra-wide) + var deviceTypes: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera] + + if #available(iOS 13.0, *) { + // Prefer virtual devices that support multiple cameras including ultra-wide + deviceTypes = [ + .builtInTripleCamera, // iPhone 11 Pro, 12 Pro, 13 Pro, 14 Pro, etc. + .builtInDualWideCamera, // iPhone 11, 13, 14 (wide + ultra-wide) + .builtInDualCamera, // iPhone 7 Plus, 8 Plus, X, XS (wide + telephoto) + .builtInWideAngleCamera // Fallback for single camera devices + ] + } + + let discoverySession = AVCaptureDevice.DiscoverySession( + deviceTypes: deviceTypes, + mediaType: .video, + position: .back + ) + + return discoverySession.devices.first }() fileprivate lazy var mic: AVCaptureDevice? = { From f3ae9678c2d6c0ee4e82ee0b021535b13d8e23b4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Feb 2026 04:06:04 +0000 Subject: [PATCH 5/5] Set default zoom to wide-angle camera (1x) on multi-camera devices On multi-camera devices with ultra-wide cameras, the default zoom now starts at the first switchover point (e.g., 2.0) which represents the main wide-angle camera at "1x" zoom - matching the default Camera app behavior. Changes: - Detect virtualDeviceSwitchOverVideoZoomFactors on iOS 13+ - Set initial zoom to first switchover point for multi-camera devices - Change _zoom(0) to _zoom(1) to maintain the default zoom level - Fallback to minimum zoom for single-camera devices This fixes the issue where the camera would start at the ultra-wide lens (0.5x zoom) instead of the expected wide-angle lens (1x zoom). https://claude.ai/code/session_01KpFgCpFkFy6d6S2V73DQqm --- Sources/CameraManager.swift | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Sources/CameraManager.swift b/Sources/CameraManager.swift index 0c50b12..3e226cc 100644 --- a/Sources/CameraManager.swift +++ b/Sources/CameraManager.swift @@ -260,7 +260,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest _updateCameraDevice(cameraDevice) _updateIlluminationMode(flashMode) _setupMaxZoomScale() - _zoom(0) + _zoom(1) _orientationChanged() } } @@ -292,7 +292,7 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest _setupOutputMode(cameraOutputMode, oldCameraOutputMode: oldValue) } _setupMaxZoomScale() - _zoom(0) + _zoom(1) } } } @@ -1562,18 +1562,40 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest fileprivate func _setupMaxZoomScale() { var maxZoom = CGFloat(1.0) var minZoom = CGFloat(1.0) + var defaultZoom = CGFloat(1.0) beginZoomScale = CGFloat(1.0) if cameraDevice == .back, let backCameraDevice = backCameraDevice { maxZoom = backCameraDevice.activeFormat.videoMaxZoomFactor minZoom = backCameraDevice.minAvailableVideoZoomFactor + + // For multi-camera devices, start at the first switchover point (main wide-angle camera) + // This represents "1x" zoom in the Camera app + if #available(iOS 13.0, *), + let switchOverFactors = backCameraDevice.virtualDeviceSwitchOverVideoZoomFactors as? [CGFloat], + let firstSwitchOver = switchOverFactors.first { + defaultZoom = firstSwitchOver + } else { + defaultZoom = minZoom + } } else if cameraDevice == .front, let frontCameraDevice = frontCameraDevice { maxZoom = frontCameraDevice.activeFormat.videoMaxZoomFactor minZoom = frontCameraDevice.minAvailableVideoZoomFactor + + // For multi-camera front devices, start at the first switchover point + if #available(iOS 13.0, *), + let switchOverFactors = frontCameraDevice.virtualDeviceSwitchOverVideoZoomFactors as? [CGFloat], + let firstSwitchOver = switchOverFactors.first { + defaultZoom = firstSwitchOver + } else { + defaultZoom = minZoom + } } maxZoomScale = maxZoom minZoomScale = minZoom + zoomScale = defaultZoom + beginZoomScale = defaultZoom } fileprivate func _checkIfCameraIsAvailable() -> CameraState {