How to set back camera zoom level to 0.5x using Swift?
Asked Answered
D

5

9

I have zoom feature working(1x onwards) for custom camera implemented using AVFoundation. This is fine till the iPhone X models. But I wanted to have 0.5x zoom in iPhone 11 and iPhone 11 Pro devices.

Code that I wrote is not working to put it to 0.5x zoom. I have tried all the possible combinations of [.builtInTripleCamera, .builtInDualWideCamera, .builtInUltraWideCamera]. The capture device with the device type .builtinUltraWideCamera is not giving 0.5 for minAvailableVideoZoomFactor.

While testing on iPhone 11, I also removed [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera] from the deviceTypes.

Appreciate any help to solve this. Below is the code which is working for 1x zoom onwards.

/// Called from -handlePinchGesture
private func zoom(_ scale: CGFloat) {
    let captureDevice = cameraDevice(.back)
            
    do {
        try captureDevice?.lockForConfiguration()

        var minZoomFactor: CGFloat = captureDevice?.minAvailableVideoZoomFactor ?? 1.0
        let maxZoomFactor: CGFloat = captureDevice?.maxAvailableVideoZoomFactor ?? 1.0
        
        if #available(iOS 13.0, *) {
            if captureDevice?.deviceType == .builtInDualWideCamera || captureDevice?.deviceType == .builtInTripleCamera || captureDevice?.deviceType == .builtInUltraWideCamera {
                minZoomFactor = 0.5
            }
        }
        zoomScale = max(minZoomFactor, min(beginZoomScale * scale, maxZoomFactor))
        captureDevice?.videoZoomFactor = zoomScale

        captureDevice?.unlockForConfiguration()
    } catch {
        print("ERROR: locking configuration")
    }
}

@objc private func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
    var allTouchesOnPreviewLayer = true
    let numTouch = recognizer.numberOfTouches
    
    for i in 0 ..< numTouch {
        let location = recognizer.location(ofTouch: i, in: view)
        let convertedTouch = previewLayer.convert(location, from: previewLayer.superlayer)
        if !previewLayer.contains(convertedTouch) {
            allTouchesOnPreviewLayer = false
            break
        }
    }
    if allTouchesOnPreviewLayer {
        zoom(recognizer.scale)
    }
}

func cameraDevice(_ position: AVCaptureDevice.Position) -> AVCaptureDevice? {
    var deviceTypes = [AVCaptureDevice.DeviceType]()
    deviceTypes.append(contentsOf: [.builtInDualCamera, .builtInTelephotoCamera, .builtInWideAngleCamera, .builtInTrueDepthCamera])
    
    if #available(iOS 13.0, *) {
        deviceTypes.append(contentsOf: [.builtInTripleCamera, .builtInDualWideCamera, .builtInUltraWideCamera])
    }

    let availableCameraDevices = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: .video, position: position).devices

    guard availableCameraDevices.isEmpty == false else {
        debugPrint("ERROR: No camera devices found!!!")
        return nil
    }

    for device in availableCameraDevices {
        if device.position == position {
            return device
        }
    }

    guard let defaultDevice = AVCaptureDevice.default(for: AVMediaType.video) else {
        debugPrint("ERROR: Can't initialize default back camera!!!")
        return nil
    }
    return defaultDevice
}
Deuce answered 14/11, 2020 at 10:5 Comment(4)
Did you find any solution updating the optical zoom to 0.5x to use the ultra wide lens in iphone 11 and above for custom camera?Southing
No. Couldn't find any solution as of now.Deuce
Please use this solution - github.com/NextLevel/NextLevel/issues/187 setting the device type to ultrawidecamera is equal to setting 0.5xSouthing
I'm not getting this to work. It's 2023. Has anyone found a solution? Nothing listed here works. I'm using an iPhone 13 and have tried .builtInDualWideCamera and .builtInUltraWideCamera but the minimumZoomLevel is still just a typical zoom level and does not match the 0.5 of level of the camera app. I know my device can do it because the camera app can. Any suggestions?Plight
A
3

The minimum "zoomFactor" property of an AVCaptureDevice can't be less than 1.0 according to the Apple Docs. It's a little confusing becuase depending on what camera you've selected, a zoom factor of 1 will be a different field of view or optical view angle. The default iPhone camera app shows a label reading "0.5" but that's just a label for the ultra wide lens in relation to the standard camera's zoom factor.

You're already getting the minZoomFactor from the device, (which will probably be 1), so you should use the device's min and max that you're reading to set the bounds of the factor you input into "captureDevice.videoZoomFactor". Then when you;ve selected the ultra wide lens, setting the zoomfactor to 1 will be as wide as you can go! (a factor of 0.5 in relation to the standard lens's field of view).

Align answered 4/4, 2021 at 22:6 Comment(0)
S
0

Updating for people who are looking to set the optical zoom level 0.5x

courtesy: https://github.com/NextLevel/NextLevel/issues/187

public class func primaryVideoDevice(forPosition position: AVCaptureDevice.Position) -> AVCaptureDevice? {
    
    // -- Changes begun
    if #available(iOS 13.0, *) {

        let hasUltraWideCamera: Bool = true // Set this variable to true if your device is one of the following - iPhone 11, iPhone 11 Pro, & iPhone 11 Pro Max
        
        if hasUltraWideCamera {

            // Your iPhone has UltraWideCamera.
            let deviceTypes: [AVCaptureDevice.DeviceType] = [AVCaptureDevice.DeviceType.builtInUltraWideCamera]
            let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: AVMediaType.video, position: position)
            return discoverySession.devices.first
            
        }
        
    }
    // -- Changes end
    

    var deviceTypes: [AVCaptureDevice.DeviceType] = [AVCaptureDevice.DeviceType.builtInWideAngleCamera] // builtInWideAngleCamera // builtInUltraWideCamera
    if #available(iOS 11.0, *) {
        deviceTypes.append(.builtInDualCamera)
    } else {
        deviceTypes.append(.builtInDuoCamera)
    }
    
    // prioritize duo camera systems before wide angle
    let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: AVMediaType.video, position: position)
    for device in discoverySession.devices {
        if #available(iOS 11.0, *) {
            if (device.deviceType == AVCaptureDevice.DeviceType.builtInDualCamera) {
                return device
            }
        } else {
            if (device.deviceType == AVCaptureDevice.DeviceType.builtInDuoCamera) {
                return device
            }
        }
    }
    
    return discoverySession.devices.first
    
}
Southing answered 9/12, 2020 at 12:45 Comment(1)
This doesn't work. My device is set to UltraWideCamera and there is still no way to get 0.5x using zoom factor.Pandorapandour
W
0

The problem is when you try to get a device of some type from discoverySession.devices it returns the default device that can be not supporting ultrawide that you need.

That was the case for me for iPhone 12Pro Max, returning only one device for Back position, reporting type BuiltInWideAngleCamera, but that was just lyes, it was the middle camera, not wide, not telephoto. Dunno why apple devs made it like that, looks like an outdated legacy architecture.

The solution was not obvious: use AVCaptureDevice.default(.builtInTripleCamera, for: .video, position: .back) to get the real device capable of zooming from 1 (your logical 0.5).

Wot answered 31/5, 2021 at 9:24 Comment(2)
"The most wide" camera of 12Pro Max is called BuiltInUltraWideCamera in code. BuiltInWideAngleCamera is "the most default" camera (or "middle" as you named). Even the only camera of iPhone 5 (and similar) is BuiltInWideAngleCamera in code. It's named so probably because the first iPhone with 2 cameras has BuiltInWideAngleCamera and BuiltInTelephotoCameraOutstanding
This is the code I'm using (with .builtInDualWideCamera or .builtInUltraWideCamera) and it still doesn't get me to 0.5 zoomPlight
M
0

We cannot set the zoom factor to less than 1.

I resolve this issue by using ".builtInDualWideCamera".

In this case, we use "Ultra-Wide Camera" with the zoom factor 2.0 (will be the default value) equal to the normal zoom factor on the "Wide Angle Camera". (minimum value will be 1.0)

If your iPhone doesn't support ".builtInDualWideCamera", we will using ".builtInWideAngleCamera" as normally and the zoom factor is 1.0 (minimum value)

 func getCameraDevices() -> [AVCaptureDevice] {
        var deviceTypes = [AVCaptureDevice.DeviceType]()
   
        if #available(iOS 13.0, *) {
            deviceTypes.append(contentsOf: [.builtInDualWideCamera])
            self.isUltraWideCamera = true
            self.defaultZoomFactor = 2.0
        }
        
        if(deviceTypes.isEmpty){
            deviceTypes.append(contentsOf: [.builtInWideAngleCamera])
            self.isUltraWideCamera = false
            self.defaultZoomFactor = 1.0
        }

        return AVCaptureDevice.DiscoverySession(deviceTypes: deviceTypes, mediaType: .video, position: .unspecified).devices
    }
Marmolada answered 3/11, 2021 at 9:15 Comment(1)
still doesn't work for me. I'm using the .builtInDualWideCamera but when I set the minimumZoom level to 1.0, it's just the normal zoom level. And of course it won't take a zoom level of 0.5 directly.Plight
A
0

I finally found the answer here: https://stackoverflow.com/a/48524598. Using the ultra-wide camera device and calling -[AVCaptureSession setSessionPreset:AVCaptureSessionPresetPhoto] resolved the issue, showing the same crop as the Photos app.

Apocynaceous answered 12/8 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.