Advanced Configurations
Customize the Overlay Appearance
To customize the appearance of the overlay, you can implement a LabelCaptureBasicOverlayDelegate.
The method brushForLabel is called every time a label is captured, and brushForField is called for each of its fields to determine the brush for the label or field.
import ScanditLabelCapture
extension YourScanViewController: LabelCaptureBasicOverlayDelegate {
func labelCaptureBasicOverlay(_ overlay: LabelCaptureBasicOverlay,
brushFor field: LabelField,
of label: CapturedLabel) -> Brush? {
return brush(for: field)
}
func labelCaptureBasicOverlay(_ overlay: LabelCaptureBasicOverlay,
brushFor label: CapturedLabel) -> Brush? {
/*
* Customize the appearance of the overlay for the full label.
* In this example, we always disable label overlays by returning nil.
*/
return nil
}
func labelCaptureBasicOverlay(_ overlay: LabelCaptureBasicOverlay,
didTap label: CapturedLabel) {
/*
* Handle user tap gestures on the label.
*/
}
private func brush(for field: LabelField) -> Brush {
let fillColor: UIColor
let strokeColor: UIColor
switch field.name {
case "<your-barcode-field-name>":
fillColor = .systemCyan.withAlphaComponent(0.5)
strokeColor = .systemCyan
case "<your-expiry-date-field-name>":
fillColor = .systemOrange.withAlphaComponent(0.5)
strokeColor = .systemOrange
default:
fillColor = .clear
strokeColor = .clear
}
return Brush(fill: fillColor, stroke: strokeColor, strokeWidth: 1)
}
}
Advanced Overlay
For more advanced use cases, such as adding custom views or implementing Augmented Reality (AR) features, you can use the LabelCaptureAdvancedOverlay. The example below creates an advanced overlay, configuring it to display a styled warning message below expiry date fields when they're close to expiring, while ignoring other fields.
import ScanditLabelCapture
// Create an advanced overlay that allows for custom views to be added over detected label fields
// This is the key component for implementing Augmented Reality features
let advancedOverlay = LabelCaptureAdvancedOverlay(labelCapture: labelCapture, view: dataCaptureView)
// Configure the advanced overlay with a delegate that handles AR content creation and positioning
advancedOverlay.delegate = self
extension YourScanViewController: LabelCaptureAdvancedOverlayDelegate {
// This method is called when a label is detected - we return nil since we're only adding AR elements to specific fields, not the entire label
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
viewFor capturedLabel: CapturedLabel) -> UIView? {
return nil
}
// This defines where on the detected label the AR view would be anchored
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
anchorFor capturedLabel: CapturedLabel) -> Anchor {
return .center
}
// This defines the offset from the anchor point for the label's AR view
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
offsetFor capturedLabel: CapturedLabel) -> PointWithUnit {
return PointWithUnit(x: .zero, y: .zero)
}
// This method is called when a field is detected in a label
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
viewFor labelField: LabelField) -> UIView? {
// We only want to create AR elements for expiry date fields that are text-based
if labelField.name.lowercased().contains("expiry") && labelField.type == .text {
// Check if scanned expiry date is too close to actual date
let daysUntilExpiry = daysUntilExpiry(from: labelField.text) // Your method
let dayLimit = 3
if daysUntilExpiry < dayLimit {
// Create and configure the AR element - a UILabel with appropriate styling
// This view will appear as an overlay on the camera feed
let warningLabel = UILabel()
warningLabel.text = "Item expires soon!"
warningLabel.textColor = .white
warningLabel.backgroundColor = .red
warningLabel.textAlignment = .center
warningLabel.layer.cornerRadius = 8
warningLabel.layer.masksToBounds = true
warningLabel.font = UIFont.boldSystemFont(ofSize: 14)
// Add padding
warningLabel.frame = CGRect(x: 0, y: 0, width: 200, height: 40)
return warningLabel
}
}
// Return nil for any fields that aren't expiry dates, which means no AR overlay
return nil
}
// This defines where on the detected field the AR view should be anchored
// .bottomCenter places it right below the expiry date text for better visibility
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
anchorFor labelField: LabelField) -> Anchor {
return .bottomCenter
}
// This defines the offset from the anchor point
func labelCaptureAdvancedOverlay(_ overlay: LabelCaptureAdvancedOverlay,
offsetFor capturedField: LabelField,
of capturedLabel: CapturedLabel) -> PointWithUnit {
return PointWithUnit(x: .zero, y: .zero)
}
}
Validation Flow
How It Works
The Validation Flow provides a guided label scanning experience. An always-present checklist shows users exactly which fields have been captured and which are still missing, making the scanning process transparent and efficient. Scanning is the fastest way to capture all label content — whether all fields are visible at once or spread across different sides of a package.
The fields shown in the checklist are driven by your Label Definition — the configuration that tells Label Capture which fields to recognize and extract. See the Label Definitions guide for details on how to set them up.
The Validation Flow overlay is a UI component built on top of Label Capture. To use it, create a LabelCaptureValidationFlowOverlay and add it to your data capture view.
| Single-Step Scan | Multi-Step Scan |
|---|---|
| All fields are visible together | Fields on different sides of the package |
![]() | ![]() |
// Create the overlay
let validationFlowOverlay = LabelCaptureValidationFlowOverlay(
labelCapture: labelCapture,
dataCaptureView: dataCaptureView
)
// Set the delegate to receive validation events
validationFlowOverlay.delegate = self
Define a Listener
When the user has verified that all fields are correctly captured and presses the finish button, the Validation Flow triggers a callback with the final results. To receive these results, implement the LabelCaptureValidationFlowOverlayDelegate protocol:
extension YourScanViewController: LabelCaptureValidationFlowDelegate {
// This is called by the validation flow overlay when a label has been fully captured and validated
func labelCaptureValidationFlowOverlay(_ overlay: LabelCaptureValidationFlowOverlay,
didCaptureLabelWith fields: [LabelField]) {
let barcodeData = fields.first { $0.name == "<your-barcode-field-name>" }?.barcode?.data
let expiryDate = fields.first { $0.name == "<your-expiry-date-field-name>" }?.text
// Handle the captured values
}
}
Required and Optional Fields
The Validation Flow clearly indicates which fields must be captured and which are optional. Required fields are visually highlighted and the flow can only be completed once all of them have been successfully scanned or manually entered. Optional fields are shown but do not block the user from finishing.
| Required Field | Optional Field |
|---|---|
| Must be captured to finish the flow | Does not block finishing |
![]() | ![]() |
Typing Hints
If neither on-device nor cloud-based scanning can capture a field, the user can always manually enter the value. To make manual input easier and reduce errors, you can configure placeholder text (typing hints) that show the expected format directly in the input field.

The field name in the label definition is used as the reference for setting placeholder text:
let validationFlowOverlaySettings = LabelCaptureValidationFlowSettings.init()
validationFlowOverlaySettings.setPlaceholderText("MM/DD/YYYY", forLabelDefinition: "Expiry Date")
validationFlowOverlay.apply(validationFlowOverlaySettings)
Customization
All text in the Validation Flow overlay can be adjusted to match your application's needs. This is useful for localization, adapting terminology, or removing text entirely for a minimal interface.
Buttons
The text on the restart, pause, and finish buttons can be customized or removed entirely.
| English (Default) | Custom Language | Company Slang | No Text |
|---|---|---|---|
let validationFlowOverlaySettings = LabelCaptureValidationFlowSettings.init()
validationFlowOverlaySettings.restartButtonText = "Borrar todo"
validationFlowOverlaySettings.pauseButtonText = "Pausar"
validationFlowOverlaySettings.finishButtonText = "Finalizar"
validationFlowOverlay.apply(validationFlowOverlaySettings)
Toasts
Toast messages appear at the top of the camera preview to inform the user about a scanning state change. The standby toast is shown when the camera is auto-paused after no label is detected for a long time. The validation toast shows how many fields have been captured so far after a scan.
| Standby | Validation |
|---|---|
let validationFlowOverlaySettings = LabelCaptureValidationFlowSettings()
validationFlowOverlaySettings.standbyHintText = "No label detected, camera paused"
validationFlowOverlaySettings.validationHintText = "data fields collected" // X/Y (X fields out of total Y) is shown in front of this string
validationFlowOverlay.applySettings(validationFlowOverlaySettings)
Field
The field state texts are shown inside the input field itself during different phases of scanning.
| Invalid Input | Scanning Text | Adaptive Scanning Text |
|---|---|---|
| Shown when manual input does not match the expected format | Shown while the camera is actively scanning | Shown while cloud-based recognition is processing |
![]() | ![]() |
let validationFlowOverlaySettings = LabelCaptureValidationFlowSettings()
validationFlowOverlaySettings.validationErrorText = "Incorrect format."
validationFlowOverlaySettings.scanningText = "Scan in progress"
validationFlowOverlaySettings.adaptiveScanningText = "Processing"
validationFlowOverlay.applySettings(validationFlowOverlaySettings)
Cloud Fallback
The Adaptive Recognition API is still in beta and may change in future versions of Scandit Data Capture SDK. To enable it on your subscription, please contact support@scandit.com.
The Adaptive Recognition Engine helps making Smart Label Capture more robust and scalable thanks to its larger, more capable model hosted in the cloud. Whenever Smart Label Capture's on-device model fails to capture data, the SDK will automatically trigger the Adaptive Recognition Engine to capture complex, unforeseen data and process it with high accuracy and reliability — avoiding the need for the user to type data manually.

Enable Adaptive Recognition by setting the mode to .auto on the label definition. This is a single extra line added to your existing label definition configuration:
let labelCaptureSettings = try LabelCaptureSettings {
LabelDefinition("Retail Item") {
CustomBarcode(
name: "Barcode",
symbologies: [.ean13UPCA, .gs1DatabarExpanded, .code128]
)
ExpiryDateText(name: "Expiry Date")
.labelDateFormat(
LabelDateFormat(
componentFormat: LabelDateComponentFormat.MDY,
acceptPartialDates: false
)
)
}
.adaptiveRecognition(.auto)
}
See AdaptiveRecognitionMode for available options.




