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.