Skip to main content

Utilities | iOS Document Scanner

Besides the scanning modules, Scanbot SDK also contains some utility classes that can reduce your workload, hence making your life easier:

Zoom- and scrollable image view (SBSDKZoomingImageScrollView)

  • You can use it as a direct UIImageView replacement.
  • You can zoom in to the image, scroll around, and when you zoom out, the image is centered within the view.
  • You can add a transparent overlay that will act like a HUD above the image view.
  • You can specify the margins that the image should maintain when zoomed out.
import UIKit
import ScanbotSDK

class ZoomingImageScrollViewSwiftViewController: UIViewController {

var imageViewWithZoom: SBSDKZoomingImageScrollView?
let image = UIImage(named: "documentImage")

override func viewDidLoad() {

// We instantiate our new class helper.
imageViewWithZoom = SBSDKZoomingImageScrollView()

// We set the desired image.
imageViewWithZoom?.image = image

// We can add some margins.
imageViewWithZoom?.margins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)

// We set an overlay on top of our image.
let overlayView = UIView()
overlayView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
imageViewWithZoom?.overlayView = overlayView

// We add our view to the subview with our desired constraints.
imageViewWithZoom?.translatesAutoresizingMaskIntoConstraints = false
if let imageViewWithZoom = imageViewWithZoom {
if let image = image {
let ratio = image.size.width / image.size.height
imageViewWithZoom.widthAnchor.constraint(equalToConstant: 320).isActive = true
imageViewWithZoom.heightAnchor.constraint(equalToConstant: 320 / ratio).isActive = true
imageViewWithZoom.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
imageViewWithZoom.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true

Camera Device (SBSDKCameraDevice)

  • Provides the available camera devices to be used with any of the Classic or RTU-UI scanner components in Scanbot SDK.
  • You can enumerate available devices using their position (front or back) and/or their type (wide, tele, ultra-wide, dual, triple).
import UIKit
import ScanbotSDK

class CameraDeviceSwiftViewController: UIViewController {

override func viewDidLoad() {

// Get all the available camera devices.
let availableDevices = SBSDKCameraDevice.availableDevices

// Get all the available back camera devices.
let availableBackDevices = SBSDKCameraDevice.availableDevices(for: .back)

// Get all the desired cameras by providing the type and position.
let availableTripeCameraBackDevices = SBSDKCameraDevice.availableDevices(for: .triple, position: .back)

// Get the default back facing camera.
let defaultBackCamera = SBSDKCameraDevice.defaultBackFacingCamera

// Get the default front facing camera.
let defaultFrontCamera = SBSDKCameraDevice.defaultFrontFacingCamera

// Get the first triple back camera and create scanner components.
if let tripleCamera = availableTripeCameraBackDevices.first {
createRTUUIScanner(with: tripleCamera)
createClassicalScanner(with: tripleCamera)


func createRTUUIScanner(with device: SBSDKCameraDevice) {

// Create the camera configuration.
let cameraConfig = SBSDKUICameraConfiguration()

// Assign the device to the camera configuration. = device

// Assemble the scanner configuration and pass the camera configuration.
let configuration =
SBSDKUIDocumentScannerConfiguration(uiConfiguration: SBSDKUIDocumentScannerUIConfiguration(),
textConfiguration: SBSDKUIDocumentScannerTextConfiguration(),
behaviorConfiguration: SBSDKUIDocumentScannerBehaviorConfiguration(),
cameraConfiguration: cameraConfig)

// Create the RTU-UI scanner, passing the scanner configuration.
let scanner = SBSDKUIDocumentScannerViewController.createNew(configuration: configuration, delegate: nil)


func createClassicalScanner(with device: SBSDKCameraDevice) {

// Create the classical scanner.
let scanner = SBSDKDocumentScannerViewController(parentViewController: self,
parentView: self.view,
delegate: nil)

// Assign the device to the scanner.
scanner?.cameraDevice = device

Image Metadata Processor (SBSDKImageMetadataProcessor)

  • Lets you read, modify and write image metadata.
import UIKit
import ScanbotSDK

class ImageMetadataProcessorSwiftViewController: UIViewController {

override func viewDidLoad() {

// Set the desired image.
let image = UIImage(named: "documentImage")
if let image {

// Convert the image to data.
let imageData = image.jpegData(compressionQuality: 0)

// Extract the metadata.
if let imageData, let oldExtractedMetadata = SBSDKImageMetadataProcessor.extractMetadata(from: imageData) {

self.printExtractedMetadata(metadata: oldExtractedMetadata, withTitle: "OLD metadata")

// Create new metadata or modify the extracted metadata.
if let injectedMetadata = SBSDKImageMetadata(with: oldExtractedMetadata.metadataDictionary) {
injectedMetadata.title = "Scanbot SDK"
injectedMetadata.originalDate = Date()

// Inject the new metadata into the image data.
let newImageData = SBSDKImageMetadataProcessor.imageDataByInjecting(metadata: injectedMetadata, into: imageData)

// Re-extract the new metadata again.
if let newImageData,
let newExtractedMetadata = SBSDKImageMetadataProcessor.extractMetadata(from: newImageData) {
self.printExtractedMetadata(metadata: newExtractedMetadata, withTitle: "NEW metadata")

// Print some of the metadata fields.
func printExtractedMetadata(metadata: SBSDKImageMetadata?, withTitle title: String) {
if let metadata {
print("Begin of \(title) *************")
print("altitude: \(String(describing: metadata.altitude))")
print("latitude: \(String(describing: metadata.latitude))")
print("longitude: \(String(describing: metadata.longitude))")
print("aperture: \(String(describing: metadata.aperture))")
print("digitalizationDate: \(String(describing: metadata.digitalizationDate?.description))")
print("exposureTime: \(String(describing: metadata.exposureTime))")
print("focalLength: \(String(describing: metadata.focalLength))")
print("focalLength35mm: \(String(describing: metadata.focalLength35mm))")
print("imageHeight: \(String(describing: metadata.imageHeight))")
print("imageWidth: \(String(describing: metadata.imageWidth))")
print("isoValue: \(String(describing: metadata.ISOValue))")
print("lensMaker: \(String(describing: metadata.lensMaker))")
print("lensModel: \(String(describing: metadata.lensModel))")
print("orientation: \(String(describing: metadata.orientation))")
print("originalDate: \(String(describing: metadata.originalDate?.description))")
print("title: \(String(describing: metadata.title))")
print("metadataDictionary: \(String(describing: metadata.metadataDictionary))")
print("End of \(title) *************")

PDF Metadata Attributes (SBSDKPDFAttributes)

  • Lets you read, modify and write a PDF document's metadata.
  • If you want to use this feature your app must import the PDFKit module.
import UIKit
import ScanbotSDK

class PDFAttributesSwiftViewController: UIViewController {

override func viewDidLoad() {

// Get the URL of your desired pdf file.
guard let url = Bundle.main.url(forResource: "document", withExtension: "pdf") else { return }

// Read the pdf metadata attributes from the PDF at the url.
let attributes = SBSDKPDFAttributes(pdfURL: url)!

// Change the pdf attributes.
attributes.title = "A Scanbot Demo PDF" = "ScanbotSDK Development"
attributes.creator = "ScanbotSDK for iOS"
attributes.subject = "A demonstration of ScanbotSDK PDF creation."
attributes.keywords = ["PDF", "Scanbot", "SDK"]

// Inject the new pdf metadata attributes into your pdf at the same url.
do {
try attributes.saveToPDFFile(at: url)
catch {
// Catch the error.

Text Orientation Recognition (SBSDKTextLayoutRecognizer)

  • Determines the orientation of text in an image.
import UIKit
import ScanbotSDK

class TextOrientationRecognizerSwiftViewController: UIViewController {

override func viewDidLoad() {

// Initialize an instance of the recognizer.
let recognizer = SBSDKTextLayoutRecognizer()

// Set the desired image.
if let image = UIImage(named: "testDocument") {

// Recognize the text orientation.
let oldOrientation = recognizer.recognizeTextOrientation(on: image)

// Handle the result.
self.printOrientationResult(orientation: oldOrientation)

// Recognize the text orientation, but request a minimum confidence of 2.0.
let orientationWithConfidence = recognizer.recognizeTextOrientation(on: image, with: 2.0)

// Handle the result.
self.printOrientationResult(orientation: orientationWithConfidence)

// Rotate the image to portrait mode if possible.
let newImage = self.rotateImageToPortraitMode(image: image, orientation: orientationWithConfidence)

// Rotate the image according to the recognized orientation.
func rotateImageToPortraitMode(image: UIImage, orientation: SBSDKTextOrientation) -> UIImage? {
switch orientation {
case .notRecognized, .lowConfidence, .up:
return image
case .right:
return image.sbsdk_imageRotatedCounterClockwise(1)
case .down:
return image.sbsdk_imageRotatedClockwise(2)
case .left:
return image.sbsdk_imageRotatedCounterClockwise(3)
return image

// Print the recognized orientation to the console.
func printOrientationResult(orientation: SBSDKTextOrientation) {
switch orientation {
case .notRecognized:
print("Text orientation was not recognized (bad image quality, etc).")
case .lowConfidence:
print("Text was recognized, but the confidence of recognition is too low.")
case .up:
print("Text is not rotated.")
case .right:
print("Text is rotated 90 degrees clockwise.")
case .down:
print("Text is rotated 180 degrees clockwise.")
case .left:
print("Text is rotated 270 degrees clockwise.")

Document Quality Analyzer (SBSDKDocumentQualityAnalyzer)

  • Analyzes the quality of text in a document on a still image.
  • The result is an enum of type SBSDKDocumentQuality.
import UIKit
import ScanbotSDK

class DocumentQualityAnalyzerSwiftViewController: UIViewController {

override func viewDidLoad() {

// Initialize the analyzer.
let analyzer = SBSDKDocumentQualityAnalyzer()

// Set the desired image.
if let image = UIImage(named: "testDocument") {

// Analyze the quality of the image.
let quality = analyzer.analyze(on: image)

// Handle the result.
self.printResult(quality: quality)

// Print the result.
func printResult(quality: SBSDKDocumentQuality) {

switch quality {
case .noDocument:
print("No document was found")
case .veryPoor:
print("The quality of the document is very poor")
case .poor:
print("The quality of the document is poor")
case .reasonable:
print("The quality of the document is reasonable")
case .good:
print("The quality of the document is good")
case .excellent:
print("The quality of the document is excellent")
@unknown default: break

Image Storages (SBSDKIndexedImageStorage, SBSDKKeyedImageStorage)

  • SBSDKIndexedImageStorage and SBSDKKeyedImageStorage are thread-safe and persistent storage for images.
  • They can be used to store and retrieve images in an array-fashioned style (indexed) or dictionary-fashioned style (keyed) to and from the device disk.
  • Both allow the usage of an encrypter which encrypts the data when writing it to the disk and decrypts the data when reading it from the disk.
  • These classes conform to the SBSDKImageStoring protocol. Custom image storage can be implemented and used within Scanbot SDK by conforming to this protocol.
import UIKit
import ScanbotSDK

class ImageStoringSwiftViewController: UIViewController {

override func viewDidLoad() {

// Initialize a folder URL to persist the images to.
let documentsURL = SBSDKStorageLocation.applicationDocumentsFolderURL.appendingPathComponent("Images", isDirectory: true)

// Create a storage location object. This will create the folder on the filesystem if neccessary.
let documentsLocation = SBSDKStorageLocation(baseURL: documentsURL)

// Initialize an indexed image storage at this location.
// The indexed image storage is an array-like storage.
let imageStorage = SBSDKIndexedImageStorage(storageLocation: documentsLocation,
fileFormat: .PNG,
encrypter: nil)!

if let image = UIImage(named: "testDocument") {
// Save an image to the storage.
let isAdded = imageStorage.add(image)
// Check the result.
print("Image added successfully: \(isAdded)")
// Check the number of images in the storage.
print("Images in storage: \(imageStorage.imageCount)")

// Create and attach an encrypter to the storage. This will encrypt the image data, before it is written to disk
// and decrypt it after it is read from disk.
// Setting the encrypter does not encrypt existing images in the storage, only the images that are added to the
// storage after setting the encrypter.
imageStorage.encrypter = SBSDKAESEncrypter(password: "xxxxx", mode: .AES256)

// Store an image from a URL. This does not load the image and has a very low memory footprint.
if let url = Bundle.main.url(forResource: "imageDocument", withExtension: "png") {
// Copy the image from the URL to the storage.
let isAdded = imageStorage.addImage(from: url)
// Check the result.
print("Image from URL was added successfully : \(isAdded)")

// Make sure that the indices are valid before moving an image from one index to another.
if imageStorage.imageCount > 1 {
// Move the image at index 1 to index 0.
let isMoved = imageStorage.moveImage(from: 1, to: 0)
print("Image was moved successfully: \(isMoved)")

// Make sure that the index is valid.
if imageStorage.imageCount > 1 {
// Remove the image at index 1.
imageStorage.removeImage(at: 1)

// The image storage is persisted on disk.
// When the images are no longer needed they should be removed to free the disk space.
// Remove all images from the storage.

Sound Controller (SBSDKSoundController)

  • Can play a beep sound, custom sounds loaded from a URL and/or a vibration.
import UIKit
import ScanbotSDK

class SoundControllerSwiftViewController: UIViewController {

override func viewDidAppear(_ animated: Bool) {

// Initialize the sound controller.
let soundController = SBSDKSoundController()

// Play the beep sound and vibrate after 2 seconds.
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {

// Beep.

// Vibrate.

// Play a sound from a custom url and vibrate after 3 seconds.
guard let url = Bundle.main.url(forResource: "sound", withExtension: "m4a") else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: {

// Play custom sound.
soundController.playCustomSound(from: url)

// Vibrate.

Want to scan longer than one minute?

Generate a free trial license to test the Scanbot SDK thoroughly.

Get your free Trial License

What do you think of this documentation?