Skip to main content

Custom capture UI

Use MBCaptureSessionView when your application must own the surrounding controls and layout. The SDK still manages camera capture and validation; your view controller manages presentation and user interaction.

Unlike MBCaptureSessionViewController, the custom view does not present the camera-permission flow for you. Request video access before creating and starting MBCaptureSessionView:

AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
DispatchQueue.main.async {
if granted {
self?.installAndStartCaptureView()
} else {
self?.showCameraPermissionMessage()
}
}
}

Import AVFoundation and include NSCameraUsageDescription in the application’s Info.plist.

Create and attach the capture view​

import UIKit
import AVFoundation
import MobaiBiometric

final class CustomCaptureViewController: UIViewController {
private var captureView: MBCaptureSessionView!

func installAndStartCaptureView() {
captureView = MBCaptureSessionView(
options: MBCaptureSessionOptions(
videoCaptureDuration: 2_000,
captureConstraints: .v3,
imageQualityLevel: .level2
)
)

captureView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(captureView)

NSLayoutConstraint.activate([
captureView.topAnchor.constraint(equalTo: view.topAnchor),
captureView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
captureView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
captureView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])

captureView.captureSessionDelegate = self
captureView.boundingBoxFaceValidatorDelegate = self
captureView.countDownDelegate = self
captureView.progressDelegate = self
captureView.onStartCapturing(sessionId: sessionId)
}

override func viewWillDisappear(_ animated: Bool) {
captureView?.onStopCapturing()
super.viewWillDisappear(animated)
}
}

Calling onStopCapturing() when the screen disappears prevents the camera from continuing when the screen is no longer visible. Call onStartCapturing(sessionId:) again before reusing the same view.

Handle session events​

extension CustomCaptureViewController: MBCaptureSessionDelegate {
func onSuccess(result: MBCaptureSessionResult) {
upload(result)
}

func onFailure(error: MBCaptureSessionError) {
showRetry(for: error)
}

func onStateChanged(captureState: MBCaptureState) { }
}

Show V3 feedback​

V2 and V3 use MBBoundingBoxFaceValidatorDelegate:

extension CustomCaptureViewController: MBBoundingBoxFaceValidatorDelegate {
func onValidating(
_ faceStatus: Result<MBFaceGeometryModel, MBFaceBoundingBoxStatus>
) {
switch faceStatus {
case .success:
feedbackLabel.text = "Hold still"

case .failure(let status):
feedbackLabel.text = feedbackText(for: status)
}
}
}

Check geometry before lighting so the user receives one clear instruction at a time. See Capture constraints and image quality for the status fields.

Show progress and countdown​

extension CustomCaptureViewController: MBCaptureProgressDelegate, MBCountDownDelegate {
func onCaptureProgress(captureProgressCounter: Float) {
progressView.progress = captureProgressCounter
}

func onCountDown(timeCounter: Int) {
countdownLabel.text = String(timeCounter)
}
}

Manual capture​

Set autoCaptureEnabled to false, then call takePicture() from your button after the SDK reports a valid face:

@IBAction private func captureButtonTapped() {
captureView.takePicture()
}

Disable the button until validation succeeds to avoid collecting an invalid capture.