Capture Reference

Add more functionality to your Lens camera on iOS

It is left to you as the developer of the app hosting LensSDK to provide all user interface affordances which will interact with the camera; capture button, flash on/off button, front/rear camera swap button, etc. As part of the initialization procedure, you pass a reference to a UIView which will host the Lens Camera preview view.

Here's an overview of the additional features supported by the camera besides standard photo and video capture.

Camera Methods

Here is a helpful list of all the optional methods to be added in extension ViewController: LensCameraDelegate.

Start recording video

lensCameraDidStartRecordingVideo()

Called when Lens starts recording video frames to disk. Use this to start a timer to show the user how long they've been recording.

Stop recording video

lensCameraDidStopRecordingVideo()

Called when Lens stops recording video frames to disk.

Generate a thumbnail

lensCameraDidGenerateVideoThumnail(fileName: String, image: UIImage, duration: TimeInterval?)

Called when Lens generates a thumbnail for the video file.

ParameterDescription
fileNameThe name of the image file, which matches the video output file name
imageA UIImage containing the thumbnail
durationThe duration of the recorded video

Recorded video completed signing

lensCameraDidGenerateTruepicVideo(_ url: URL)

Called when the recorded video data has completed C2PA processing.

ParameterDescription
urlThe URL to which the recorded and signed video data was saved

Recorded video failed to be signed

lensCameraDidGenerateUnsignedVideo(_ url: URL)

Called if signing of the recorded video fails C2PAProcessing.

ParameterDescription
urlThe URL to which the unsigned recorded video data was saved

New zoom factor

lensCameraChangedZoomFactor(_ zoomFactor: CGFloat?, error: Error?)

This method provides the new zoom factor, which can be used as an informational display for the user - 1X, 2X, 3X, etc.

ParameterDescription
zoomFactorThe new zoom factor

Get focal point

lensCameraDidFocusOnPoint(_ point: CGPoint?, error: Error?)

Confirms the actual point within the camera's viewport that was used as the new focal point. Auto exposure metering is also applied to the new point.

New instance

lensCameraDidChangeTo(_ device: LensCaptureDevice?, error: Error?)

Provides a new instance of LensCaptureDevice upon successful change. This will be the same as LensCamera.CurrentDevice, so there is no need to cache this value.

Get flash mode

lensCameraDidSetFlashMode(_ mode: LensCaptureDevice.FlashMode?, error: Error?

Provides the new flash mode that was selected.

Get manual focus area

lensCameraSubjectAreaDidChange()

If focus has been set manually, this method is called when subject area changes are detected, such as lighting changes, substantial movement, etc.

Audio methods

Like video, capturing audio through Lens is as simple as calling startAudioCapture and stopAudioCapture:

public func startAudioCapture

public func stopAudioCapture

If you intend to record audio, you should add the appropriate Privacy keys to your app's Info.plist.

KeyValue (example)
Privacy - Microphone Usage DescriptionAPPNAME will use the microphone when recording videos or audio.

or

<key>NSMicrophoneUsageDescription</key>
<string>APPNAME will use the microphone when recording videos or audio.</string>

Lens notifies your delegate of certain lifecycle events once recording starts:

// Called when Lens starts recording audio.
func lensCameraDidStartRecordingAudio() {}

// Called multiple times a second with the instantaneous average power level in decibels per channel.
func lensCameraDidMeasureAudioLevels(_ levels: [Float]) {}
    
// Called when Lens stops recording audio.
func lensCameraDidStopRecordingAudio() {}
    
// Called when Lens signs the recorded audio file.
// - url: the final URL of the signed media
// - duration: the duration of the recording. To get the number of seconds, use duration.seconds
// - error: if an error was encountered during signing, it will be return here
func lensCameraDidGenerateSignedAudio(_ url: URL?, duration: CMTime?, error: Error?) {}

The delegate method lensCameraDidMeasureAudioLevels can be used to display an audio meter in your app, useful to let the user know that audio is being picked up by the device's microphones. Multiple channels are supported.

The example code below normalizes the incoming levels and masks a UIImageView with a fill percentage calculated from the levels. Note that in multi-channel situations, this code only operates on the first channel. You may want to modify this code to account for more than one channel.

func lensCameraDidMeasureAudioLevels(_ levels: [Float]) {
    // Normalize the level to a value between 0 and 1
    let normalizedLevel = min(max(levels[0], -60), 0) + 60
    let fillPercentage: CGFloat = CGFloat(normalizedLevel / 60)

    // Update the mask of the white microphone image view
    let maskLayer = CALayer()
    maskLayer.backgroundColor = UIColor.black.cgColor
    let maskHeight = audioMeterFilledImageView.bounds.height * fillPercentage
    maskLayer.frame = CGRect(x: 0, y: audioMeterFilledImageView.bounds.height - maskHeight, width: audioMeterFilledImageView.bounds.width, height: maskHeight)
    DispatchQueue.main.async {
        self.audioMeterFilledImageView.layer.mask = maskLayer
        self.audioMeterFilledImageView.setNeedsDisplay()
    }
}

Add Camera Controls

The following properties are available from lensCamera.sharedInstance.currentDevice. The capabilities of the default capture device are encapsulated by the following:

public struct LensCaptureDevice {
    // The value of this property is a BOOL indicating whether the receiver has a flash.
    // The receiver's flashMode property can only be set when this property returns true.
    public var hasFlash: Bool { get }

    // Indicates whether the receiver's flash is currently available for use.
    // The flash may become unavailable if, for example, the device overheats and needs to cool off.
    public var isFlashAvailable: Bool { get }

    // The value of this property is a Position indicating where the receiver's device
    // is physically located on the system hardware.
    public var position: LensCaptureDevice.Position { get }

    // Indicates the mode of the flash on the receiver's device, if it has one.
    public var flashMode: LensCaptureDevice.FlashMode { get }

    // The value of this property is a BOOL indicating whether the receiver can zoom
    public var canZoom: Bool { get }

    // Controls zoom level of image outputs.
    public var videoZoomFactor: CGFloat { get }
}

Zoom

To enable pinch-to-zoom, create a UIPinchGestureRecognizer and add it to your cameraPreviewView:

let zoom = UIPinchGestureRecognizer(target: self, action: #selector(handleZoom(_:)))
cameraPreviewView.addGestureRecognizer(zoom)

In your zoom handler, call LensCamera's zoom(with:) method, and pass the sender property:

@objc private func handleZoom(_ sender: UIPinchGestureRecognizer) {
    self.camera?.zoom(with: sender)
}

Implement the optional delegate method to be notified when the zoom factor changes so that you can update your user interface. This is always delivered on the main queue:

public protocol LensCameraDelegate: AnyObject {
    // Called when LensCamera preview view changes zoom level.
    func lensCameraDidChangeZoomLevel(_ zoom: CGFloat?, error: Error?)
}

Focus

To enable tap-to-focus, create a UITapGestureRecognizer and add it to your cameraPreviewView:

let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapToFocus(_:)))
cameraPreviewView.addGestureRecognizer(tap)

In your tap handler, call LensCamera's focus(with:)method, and pass the sender property:

@objc private func handleTapToFocus(_ sender: UITapGestureRecognizer) {
    self.camera?.focus(with: sender)
}

This tells LensCamera to set the device's focus point of interest, and to begin auto exposure for the selected point. You can use this method to add a focus reticle to the camera view. See the example integration code for more.

Implement the optional delegate method to be notified when LensCamera focuses on the selected point, or when an error occurs during focus and exposure. This is always delivered on the main queue:

public protocol LensCameraDelegate: AnyObject {
      // Called when the focus point of interest changes.
    func lensCameraDidFocusOnPoint(_ point: CGFloat?, error: Error?)
}

If an error is returned, it will be of type LensCameraError.setFocusFailed.

Enable flash

Flash availability depends on the current capture device's capabilities. lensFlashMode can only be set to the values contained in supportedFlashModes.

LensFlashMode has three possible states:

public enum FlashMode: Int {
    // Indicates that the flash should always be off. Default.
    case off = 0
    // Indicates that the flash should always be on.
    case on = 1
    // Indicates that the flash should be used automatically depending on light conditions.
    case auto = 2
}

The current state of flash can be obtained or set with this property:

self.camera?.currentDevice?.flashMode

To cycle through the available flash modes, call:

self.camera?.toggleFlash()

Implement the optional delegate method to be notified when Lens changes the flash mode, or when an error occurs. This is always delivered on the main queue:

public protocol LensCameraDelegate: AnyObject {
    func lensCameraDidSetFlashMode(_ mode: LensCaptureDevice.FlashMode?, error: Error?)
}

Switch cameras

You can add a control to your user interface to switch between front and back cameras. By default, the back camera is activated when the session is configured.

public enum Position: Int {
    case unspecified = 0
    case back = 1   // default
    case front = 2
}

The currently selected position can be obtained via this property:

self.camera?.currentDevice?.devicePosition

To toggle between front and back devices, call:

self.camera?.changeCamera()

Implement the optional delegate method to be notified when Lens switches between back and front cameras, or when an error occurs. This is always delivered on the main queue:

public protocol LensCameraDelegate: AnyObject {
    func lensCameraDidChangeTo(_ device: LensCaptureDevice?, error: Error?)
}

Location Accuracy

Your customer's location at the time they capture a photo or video is an important piece of provenance. The more accurately that location can be determined, the better. The device's location can be determined in a number of ways: directly from a GPS, or approximated by cell phone tower or WiFi network triangulation, and each have varying degrees of accuracy. Moving around, or going from outdoors to indoors can also affect accuracy. This variance is encoded as a radius of uncertainty about the device, measured in meters. The threshold is the maximum accuracy required to enable the camera. After that threshold is passed, the user may begin taking photos. Lens will continue to get a more accurate reading even after the threshold is passed, often down to 3-4m of accuracy. The user experience would be negatively affected if we prevented capture until that level of accuracy was achieved because it can take 30 seconds or more.

The default location accuracy used by Lens is 1500 meters, or approximately 1 mile. To lower this call the following on LensCamera:

@discardableResult
public func setMaximumLocationAccuracy(_ newValue: Double) -> Bool

The range of valid values is from 3 to 1500 meters. If a value is set beyond these bounds, the function will return false, otherwise true is returned. Note that setting this value very low may increase the time it takes for Lens to get an accurate location reading, and enable the camera for capture.

Reduce accuracy

For cases where the accuracy requires a higher threshold, the 1500 meters limitation can be removed by setting the allowsImprecise option to true while configuring the LensCamera session:

    guard let camera = self.camera else { return }
    camera.configureSession(previewView: cameraPreviewView, fillView: false, apiKey: apiKey, delegate: self, locationMode: .required, allowsImprecise: true)

Setting the allowsImprecise value to true will widen the threshold to 20,000 meters. The default value of allowsImprecise is false.

Make location optional

For camera use that does not require the location information. The SDK offers an optional or none setting. The default setting is required if nothing has been specified.

public enum LensLocationMode: Int {
    case required
    case optional
    case none
}

Setting the LensLocationMode to optional will allow the SDK to provide the Location data if the device's location permission is enabled by the user. If the location permission on the device is disabled, location information will not be provided.

    guard let camera = self.camera else { return }
    camera.configureSession(previewView: cameraPreviewView, fillView: false, apiKey: api_key, delegate: self, locationMode: LensLocationMode.optional)

Disabling location data from signing can be performed by setting LensLocationMode to none. This will disregard any location data during signing regardless of the location permission setting.

    guard let camera = self.camera else { return }
    camera.configureSession(previewView: cameraPreviewView, fillView: false, apiKey: api_key, delegate: self, locationMode: LensLocationMode.none)

Offline Capture

Lens offers out of the box support for capturing and signing photos and videos offline, which can be uploaded to your server or directly to the Lens API when the device regains connectivity. To learn more about the differences between online and offline capture, see Offline Capture.