Skip to content

Instantly share code, notes, and snippets.

@ghermanowski
Created June 9, 2022 17:32
Show Gist options
  • Save ghermanowski/b586f903b56f6928e5a442608144be2e to your computer and use it in GitHub Desktop.
Save ghermanowski/b586f903b56f6928e5a442608144be2e to your computer and use it in GitHub Desktop.
How to Capture Text from the Camera in SwiftUI 3

How to Capture Text from the Camera in SwiftUI 3

To create a button for this purpose, you need a class that adopts the protocols UIResponder and UIKeyInput. The required variable hasText and method deleteBackward are not used so you can just ignore them.

class ScanTextResponder: UIResponder, UIKeyInput {
	init(title: Binding<String>) {
		_title = title
	}

	@Binding var title: String

	var canCaptureTextFromCamera: Bool {
		canPerformAction(#selector(captureTextFromCamera(_:)), withSender: self)
	}

	var hasText = false

	func insertText(_ text: String) {
		title = text
	}

	func deleteBackward() {}
}

Because the feature does not work on older devices, you should add a condition like canCaptureTextFromCamera, to not show the button on these devices. Then you just need to use the responder class in a view like this:

struct ScanTextButton: View {
	init(title: Binding<String>) {
		self.responder = ScanTextResponder(title: title)
	}

	private let responder: ScanTextResponder

	var body: some View {
		if responder.canCaptureTextFromCamera {
			Button {
				responder.captureTextFromCamera(nil)

				// Allows dismissal of the camera after multiple presses
				let backup = responder.title
				responder.title = ""
				responder.title = backup
			} label: {
				Label("Scan Text", systemImage: "text.viewfinder")
			}
		}
	}
}

The last three lines in the button action are not necessary, but I have found that otherwise you can't dismiss the camera view after pressing the button multiple times while the camera is already visible. I haven't found a better way to do that.

UIViewRepresentable

Alternatively, there is also the possibility to integrate a UIViewRepresentable. The advantage of that is, that the button label and icon are preconfigured which includes localisation. The disadvantage is that you have to use UIKit if you want to style the button.

struct UIScanTextButton: UIViewRepresentable {
	let coordinator: ScanTextResponder

	func makeCoordinator() -> ScanTextResponder {
		coordinator
	}

	func makeUIView(context: Context) -> UIButton {
		UIButton(primaryAction: .captureTextFromCamera(responder: context.coordinator, identifier: nil))
	}

	func updateUIView(_ uiView: UIViewType, context: Context) {}
}

You can use that in your SwiftUI view like this:

UIScanTextButton(coordinator: responder)
			.fixedSize()

The fixedSize() modifier ensures that the view does not cover the whole screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment