Toggle

Hi, I am creating an app that recognises and extracts text from image.

The line of code request.recognitionLevel = .accurate can also take the value of request.recognitionLevel = .fast .

I would like to build a toggle which switches the value of request.recognitionLevel between fast and accurate however I am unsure on how to do this.

Any help would be greatly appreciated.

Many thanks for your time.

My code is shown below:

import SwiftUI

import UIKit

import Vision



class ViewController2: UIViewController {



    @IBOutlet weak var imageView: UIImageView!

    @IBOutlet weak var textView: UITextView!

    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!

    @IBOutlet weak var button: UIButton!

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        button.backgroundColor = .systemCyan // Background colour of the select photo button

        button.setTitle("Select Photo", for: .normal) // Button text

        button.setTitleColor(.white, for: .normal) // Button text Colour

        

        // Rounding the edges of the 'Select Photo' button:

        button.layer.cornerRadius = 25

        button.layer.borderWidth = 20

        button.layer.borderColor = UIColor.systemCyan.cgColor

        

        // Rounding the edges of the 'Share' button:

        shareButton.layer.cornerRadius = 25

        shareButton.layer.borderWidth = 10

        shareButton.layer.borderColor = UIColor.systemCyan.cgColor

        

        stopAnimating() // Activity indicator disappears

    }

    

    // Defining the activity indicators show and spin

    private func startAnimating() {

        self.activityIndicator.startAnimating()

    }

    

    // Defining the activity indicators stop and hide

    private func stopAnimating(){

        self.activityIndicator.stopAnimating()

    }



    

    @IBAction func selectPhotoButton(_ sender: Any) { // When selectPhoto button is pressed,

        SelectPhotoButtonPressed() // run the function SelectPhotoButtonPressed.

    }

    

    

    private func SelectPhotoButtonPressed(){

        

        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){

            

            let imageSelector = UIImagePickerController() // Apple's interface for taking pictures and loading items from camera roll

            imageSelector.sourceType = .photoLibrary // Opens the device's photo library

            imageSelector.delegate = self // Leaves the UIImagePickerController when a picture is selected

            self.present(imageSelector, animated: true, completion: nil) // Present the imageSelector

            

        }

    }

    

    

    // Text Recognition

    

    

    // Create request

    var request = VNRecognizeTextRequest(completionHandler: nil)

    

    private func VisionOCR(image: UIImage?){

        

        var textString = "" // Variable textString = string

        

        // Create completion handler

        request = VNRecognizeTextRequest(completionHandler: { (request, error)in  // Locates all the text in the image.

           

            // Results are in the request

            guard let results = request.results as?[VNRecognizedTextObservation] else {fatalError("Recieved Invalid Observation")}

            

            for visionResult in results{

                guard let recognisedText = visionResult.topCandidates(1).first else{ // Text stored in chronological order.

                    print("No text")

                    continue

                }

                

                textString += "\n\(recognisedText.string)"

                

                DispatchQueue.main.async{ // FIFO queue

                    self.stopAnimating() // Hide the activityIndicator

                    self.textView.text = textString // Assign the textView the recoginsed text

                }

                

            }

            

        })

        

        

        // Properties

        

        request.minimumTextHeight = 0.03125 // Default mimnimum height for text to be recognised in comparison to image, (1/32).

        request.recognitionLevel = .accurate // Choice between accurate and fast.

        request.recognitionLanguages = ["en_UK", "en-US", "fr-FR", "it-IT", "de-DE", "es-ES", "pt-BR", "zh-Hans", "zh-Hant", "yue-Hans", "yue-Hant", "ko-KR", "ja-JP", "ru-RU", "uk-UA"] // Recognisable languages.

        request.usesLanguageCorrection = true // Applies language correction.

        

        let requests = [request]

        

        

        // Request handler

        

        DispatchQueue.global(qos: .userInitiated).async{  // FIFO queue, qos (quality of service) determines priotity for scheduling tasks

            

            guard let img = image?.cgImage else {fatalError("Missing image to scan")} // Variable image = computer generated image

            

            // Create request handler

            let requestHandler = VNImageRequestHandler(cgImage: img, options: [:])  // Performs Vision requests

            

            // Send request to request handler

            try? requestHandler.perform(requests) // Schedules Vision requests to be performed

            

        }

        

    }

    

}





extension ViewController2: UIImagePickerControllerDelegate, UINavigationControllerDelegate{

    

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey :Any]){

        

        picker.dismiss(animated: true, completion: nil)

        

        startAnimating()

        self.textView.text = ""

        

        let image = info[UIImagePickerController.InfoKey.originalImage]as?UIImage

        self.imageView.image = image

        

        VisionOCR(image: image)

    }

}





public struct storyboardview2: UIViewControllerRepresentable{

    public func makeUIViewController(context content: Context) -> UIViewController {

        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

        let controller = storyboard.instantiateViewController(identifier: "selectPhoto")

        return controller

    }

    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {

        

    }

}

I would do the following.

create a State var accuracy (can be either .fast or .accurate)

In the button action, you set accuracy to the relevant value.

And replace

        request.recognitionLevel = .accurate // Choice between accurate and fast.

with

        request.recognitionLevel = accuracy

I have added a Pop Up Button to my storyboard and added the following to my code however now I am receiving an error stating 'Type of expression is ambiguous without more context'.

Any ideas would be greatly appreciated.

        func changeRecognitionLevel(_ sender: UIControl) {

            guard let selectedItem = sender.selectedItem else {

                return

            }

            switch selectedItem.identifier!.rawValue {

            case "fast":

                request.recognitionLevel = .fast

            default:

                request.recognitionLevel = .accurate

            }

        }

On which line exactly do you get the error ?

Try adding  VNRequestTextRecognitionLevel

        func changeRecognitionLevel(_ sender: UIControl) {

            guard let selectedItem = sender.selectedItem else {  return  }

            switch selectedItem.identifier!.rawValue {
                case "fast":
                    request.recognitionLevel = VNRequestTextRecognitionLevel.fast

                default:
                    request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
            }

        }

Hi,

The error comes on the 2nd line guard let selectedItem = sender.selectedItem else { return }.

Many thanks for your help.

You declare sender as UIControl which has no selectedItem property.

        func changeRecognitionLevel(_ sender: UIControl) {

UIControl has no What is the precise type of the sender ?

You should either

  • change the func definition to with exact type
        func changeRecognitionLevel(_ sender: CorrectType) {
  • cast with
guard let selectedItem = (sender as? CorrectType).selectedItem

Hi,

I replaced the code as shown below. However, CorrectType is not defined. The error message states "Cannot find type 'CorrectType' in scope." How and where do I define it?

Many thanks for your help.

        func changeRecognitionLevel(_ sender: CorrectType) {



            guard let selectedItem = (sender as? CorrectType).selectedItem else {  return  }



            switch selectedItem.identifier!.rawValue {

                case "fast":

                    request.recognitionLevel = VNRequestTextRecognitionLevel.fast



                default:

                    request.recognitionLevel = VNRequestTextRecognitionLevel.accurate

            }



        }

I wrote CorrectType because you don't tell what type it is ! Of course, you must use the real type of the sender (the correct type 😆).

Could you show the code where you call changeRecognitionLevel ?

Hi, my code is shown below:

I have changed correct type to UIControl which I believe is the correct type however I still receive an error on the line guard let selectedItem = (sender as? UIControl).selectedItem else {  return  }

What I have on my storyboard is called a 'Pop Up Button' however I haven't/am unsure on how to define this button in the code. I have also created 2 elements on the storyboard in the pop up button called accurate and fast.

Many thanks for your help.

import SwiftUI
import UIKit
import Vision


class ViewController2: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var shareButton: UIButton!

    override func viewDidLoad() {

        super.viewDidLoad()

        button.backgroundColor = .systemCyan // Background colour of the select photo button
        button.setTitle("Select Photo", for: .normal) // Button text
        button.setTitleColor(.white, for: .normal) // Button text Colour

        

        // Rounding the edges of the 'Select Photo' button:
        button.layer.cornerRadius = 25
        button.layer.borderWidth = 20
        button.layer.borderColor = UIColor.systemCyan.cgColor
        

        // Rounding the edges of the 'Share' button:
        shareButton.layer.cornerRadius = 25
        shareButton.layer.borderWidth = 10
        shareButton.layer.borderColor = UIColor.systemCyan.cgColor
        

        stopAnimating() // Activity indicator disappears
    }


    // Defining the activity indicators show and spin
    private func startAnimating() {
        self.activityIndicator.startAnimating()
    } 

    // Defining the activity indicators stop and hide
    private func stopAnimating(){
        self.activityIndicator.stopAnimating()
    }

    @IBAction func selectPhotoButton(_ sender: Any) { // When selectPhoto button is pressed,
        SelectPhotoButtonPressed() // run the function SelectPhotoButtonPressed.
    }

    private func SelectPhotoButtonPressed(){
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){

            let imageSelector = UIImagePickerController() // Apple's interface for taking pictures and loading items from camera roll
            imageSelector.sourceType = .photoLibrary // Opens the device's photo library
            imageSelector.delegate = self // Leaves the UIImagePickerController when a picture is selected
            self.present(imageSelector, animated: true, completion: nil) // Present the imageSelector
        }
    }
    

    // Text Recognition

    // Create request

    var request = VNRecognizeTextRequest(completionHandler: nil)
    private func VisionOCR(image: UIImage?){
        var textString = "" // Variable textString = string

        // Create completion handler
        request = VNRecognizeTextRequest(completionHandler: { (request, error)in  // Locates all the text in the image.

            // Results are in the request
            guard let results = request.results as?[VNRecognizedTextObservation] else {fatalError("Recieved Invalid Observation")}

            

            for visionResult in results{
                guard let recognisedText = visionResult.topCandidates(1).first else{ // Text stored in chronological order.
                    print("No text")
                    continue
                }

                textString += "\n\(recognisedText.string)"

                DispatchQueue.main.async{ // FIFO queue
                    self.stopAnimating() // Hide the activityIndicator
                    self.textView.text = textString // Assign the textView the recoginsed text
                }
            }
        })

        // Properties
        request.minimumTextHeight = 0.03125 // Default mimnimum height for text to be recognised in comparison to image, (1/32).
        request.recognitionLevel = .accurate // Choice between accurate and fast.
        var recognitionLevel: VNRequestTextRecognitionLevel = .accurate {

            didSet {
                VisionOCR(image: image)
            }
        }

        request.recognitionLanguages = ["en_UK", "en-US", "fr-FR", "it-IT", "de-DE", "es-ES", "pt-BR", "zh-Hans", "zh-Hant", "yue-Hans", "yue-Hant", "ko-KR", "ja-JP", "ru-RU", "uk-UA"] // Recognisable languages.

        request.usesLanguageCorrection = true // Applies language correction.

        let requests = [request]


        func changeRecognitionLevel(_ sender: UIControl) {

            guard let selectedItem = (sender as? UIControl).selectedItem else {  return  }

            switch selectedItem.identifier!.rawValue {

                case "fast":

                    request.recognitionLevel = VNRequestTextRecognitionLevel.fast

                default:

                    request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
            }
        }


        // Request handler
        DispatchQueue.global(qos: .userInitiated).async{  // FIFO queue, qos (quality of service) determines priotity for scheduling tasks
            guard let img = image?.cgImage else {fatalError("Missing image to scan")} // Variable image = computer generated image


            // Create request handler
            let requestHandler = VNImageRequestHandler(cgImage: img, options: [:])  // Performs Vision requests


            // Send request to request handler
            try? requestHandler.perform(requests) // Schedules Vision requests to be performed 

        }
    }
}

extension ViewController2: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey :Any]){
        picker.dismiss(animated: true, completion: nil)
        startAnimating()
        self.textView.text = ""
        let image = info[UIImagePickerController.InfoKey.originalImage]as?UIImage
        self.imageView.image = image
        VisionOCR(image: image)
    }
}

public struct storyboardview2: UIViewControllerRepresentable{
    public func makeUIViewController(context content: Context) -> UIViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let controller = storyboard.instantiateViewController(identifier: "selectPhoto")
        return controller
    }
    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    }
}

You don't answer questions, so it is very hard to help.

  • Where do you USE changeRecognitionLevel ? Somewhere you must have (here, aControl is a generic name as I don't know what you use; we need to know how this aControl is declared)
changeRecognitionLevel(aControl)
  • UIControl is probably not the exact type. The sender is a subclass of UIControl, which has a selectedItem property. Which one is it ?

The change RecognitionLevel call is shown below.

    @IBAction func Accuracy(_ sender: Accuracy) {

        changeRecognitionLevel()

    }

I have therefore changed the function as shown below:

    func changeRecognitionLevel(_ sender: Accuracy) {
        guard let selectedItem = (sender as? Accuracy).selectedItem else {  return  }
        switch selectedItem.identifier!.rawValue {
            case "fast":
                request.recognitionLevel = VNRequestTextRecognitionLevel.fast
            default:
                request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
        }
    }

As to your second question, I do not understand where I will find the selectedItem property.

Many thanks for your help.

I have rewritten the code as shown below, however there is still the same error:

The error comes on the second line of code and states 'Type of expression is ambiguous without more context'.

Any thoughts on how to fix this will be greatly appreciated.

    @IBAction func Accuracy(_ sender: UIMenuElement) {
        guard let selectedItem = sender.selectedItem else { return }
        switch selectedItem.identifier!.rawValue {
            case "fast":
                request.recognitionLevel = VNRequestTextRecognitionLevel.fast
            default:
                request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
        }
    }

This doesn't answer the question…

What is Accuracy ? Is it a class of yours ? If so, please show.

I don't understand how this could compile:

    @IBAction func accuracy(_ sender: Accuracy) {  // Should be better to start with lowercase
        changeRecognitionLevel()
    }

changeRecognitionLevel expect a parameter …

Note: giving the same name to the func and the type is not the best idea. In addition, func name should start with lowercase, according to Swift practices.

.

I do not understand where I will find the selectedItem property.

You have to check if Accuracy clas has such a property.

No, accuracy is not a class, I just thought thatI had to give the action a name.

My code is shown below:

import SwiftUI
import UIKit
import Vision
class ViewController2: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var shareButton: UIButton!
    @IBAction func accuracy(_ sender: UIEditMenuInteraction) {
        if let selectedItem = sender.menuItems.first?.selectedItem {
            switch selectedItem.identifier!.rawValue {
                case "fast":
                    request.recognitionLevel = VNRequestTextRecognitionLevel.fast
                default:
                    request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
            }
        }
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        button.backgroundColor = .systemCyan // Background colour of the select photo button
        button.setTitle("Select Photo", for: .normal) // Button text
        button.setTitleColor(.white, for: .normal) // Button text Colour
        // Rounding the edges of the 'Select Photo' button:
        button.layer.cornerRadius = 25
        button.layer.borderWidth = 20
        button.layer.borderColor = UIColor.systemCyan.cgColor
        // Rounding the edges of the 'Share' button:
        shareButton.layer.cornerRadius = 25
        shareButton.layer.borderWidth = 10
        shareButton.layer.borderColor = UIColor.systemCyan.cgColor
        stopAnimating() // Activity indicator disappears
    }
    // Defining the activity indicators show and spin
    private func startAnimating() {
        self.activityIndicator.startAnimating()

    }
    // Defining the activity indicators stop and hide
    private func stopAnimating(){
        self.activityIndicator.stopAnimating()
    }
    @IBAction func selectPhotoButton(_ sender: Any) { // When selectPhoto button is pressed,
        SelectPhotoButtonPressed() // run the function SelectPhotoButtonPressed.
    }
    private func SelectPhotoButtonPressed(){
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
            let imageSelector = UIImagePickerController() // Apple's interface for taking pictures and loading items from camera roll
            imageSelector.sourceType = .photoLibrary // Opens the device's photo library
            imageSelector.delegate = self // Leaves the UIImagePickerController when a picture is selected
            self.present(imageSelector, animated: true, completion: nil) // Present the imageSelector
        }
    }
    // Text Recognition
    // Create request
    var request = VNRecognizeTextRequest(completionHandler: nil)
    private func VisionOCR(image: UIImage?){
        var textString = "" // Variable textString = string
        // Create completion handler
        request = VNRecognizeTextRequest(completionHandler: { (request, error)in  // Locates all the text in the image.
            // Results are in the request
            guard let results = request.results as?[VNRecognizedTextObservation] else {fatalError("Recieved Invalid Observation")}
            for visionResult in results{
                guard let recognisedText = visionResult.topCandidates(1).first else{ // Text stored in chronological order.
                    print("No text")
                    continue
                }
                textString += "\n\(recognisedText.string)"
                DispatchQueue.main.async{ // FIFO queue
                    self.stopAnimating() // Hide the activityIndicator
                    self.textView.text = textString // Assign the textView the recoginsed text
                }
            }
        })
        // Properties
        request.minimumTextHeight = 0.03125 // Default mimnimum height for text to be recognised in comparison to image, (1/32).
        request.recognitionLevel = .accurate // Choice between accurate and fast.
        request.recognitionLanguages = ["en_UK", "en-US", "fr-FR", "it-IT", "de-DE", "es-ES", "pt-BR", "zh-Hans", "zh-Hant", "yue-Hans", "yue-Hant", "ko-KR", "ja-JP", "ru-RU", "uk-UA"] // Recognisable languages.
        request.usesLanguageCorrection = true // Applies language correction.
        let requests = [request]
        //changeRecognitionLevel(Accuracy)
//        func changeRecognitionLevel(_ sender: Accuracy) {
//            guard let selectedItem = (sender as? Accuracy).selectedItem else {  return  }
//            switch selectedItem.identifier!.rawValue {
//                case "fast":
//                    request.recognitionLevel = VNRequestTextRecognitionLevel.fast
//                default:
//                    request.recognitionLevel = VNRequestTextRecognitionLevel.accurate
//            }
//        }
        // Request handler
        DispatchQueue.global(qos: .userInitiated).async{  // FIFO queue, qos (quality of service) determines priotity for scheduling tasks
            guard let img = image?.cgImage else {fatalError("Missing image to scan")} // Variable image = computer generated image
            // Create request handler
            let requestHandler = VNImageRequestHandler(cgImage: img, options: [:])  // Performs Vision requests
            // Send request to request handler
            try? requestHandler.perform(requests) // Schedules Vision requests to be performed
        }
    }
}
extension ViewController2: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey :Any]){
        picker.dismiss(animated: true, completion: nil)
        startAnimating()
        self.textView.text = ""
        let image = info[UIImagePickerController.InfoKey.originalImage]as?UIImage
        self.imageView.image = image
        VisionOCR(image: image)
    }
}
public struct storyboardview2: UIViewControllerRepresentable{
    public func makeUIViewController(context content: Context) -> UIViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let controller = storyboard.instantiateViewController(identifier: "selectPhoto")
        return controller
    }
    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    }
}

Just for your reference, the storyboard appears as shown below:

And the error message appears as shown below:

There are several things I do not understand in your code.

  • You defined an IBAction.
     @IBAction func accuracy(_ sender: UIEditMenuInteraction) { … }
  • To which object in the storyboard is it connected to ?

The way to define UIEditMenuInteraction is not with an IBAction, but

  • creating UIEditMenuInteraction in code

  • addInteraction to the object that will react to a longPress for instance.

  • create a longPress gestureRecognizer ; its selector will contain the code to execute on longPress

  • addGestureRecognizer to this object

  • conform your ViewController to UIEditMenuInteractionDelegate protocol

  • write a func editMenuInteraction that will create menus and manage their selection

See complete example in

  • Apple's doc (UIEditMenuInteraction in XCode documentation)

or

Hi, thanks for your reply but I don't particularly want an object to interact on a long press. What I am after is something that will look like this:

And will interact with the user on tap like this:

The problem js I josh to not know how to implement something like this.

he images I have uploaded are of a NSPopUpButton however I believe they are only used in Mac OS and so I can't use them in my iOS app.

Any thoughts on how I could implement something like this would be great.

Many thanks

I have rewritten the code for my button as shown below. It no longer crashes however I can not see the button / it is not visible on the simulator. Any thoughts would be great. Many thanks

import SwiftUI
import UIKit
import Vision
class ViewController2: UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var textView: UITextView!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var shareButton: UIButton!
    override func viewDidLoad() {
        super.viewDidLoad()
        button.backgroundColor = .systemCyan // Background colour of the select photo button
        button.setTitle("Select Photo", for: .normal) // Button text
        button.setTitleColor(.white, for: .normal) // Button text Colour
        // Rounding the edges of the 'Select Photo' button:
        button.layer.cornerRadius = 25
        button.layer.borderWidth = 20
        button.layer.borderColor = UIColor.systemCyan.cgColor
        // Rounding the edges of the 'Share' button:
        shareButton.layer.cornerRadius = 25
        shareButton.layer.borderWidth = 10
        shareButton.layer.borderColor = UIColor.systemCyan.cgColor
        stopAnimating() // Activity indicator disappears
    }
    // Defining the activity indicators show and spin
    private func startAnimating() {
        self.activityIndicator.startAnimating()
    }
    // Defining the activity indicators stop and hide
    private func stopAnimating(){
        self.activityIndicator.stopAnimating()
    }
    @IBAction func selectPhotoButton(_ sender: Any) { // When selectPhoto button is pressed,
        SelectPhotoButtonPressed() // run the function SelectPhotoButtonPressed.
    }
    private func SelectPhotoButtonPressed(){
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
            let imageSelector = UIImagePickerController() // Apple's interface for taking pictures and loading items from camera roll
            imageSelector.sourceType = .photoLibrary // Opens the device's photo library
            imageSelector.delegate = self // Leaves the UIImagePickerController when a picture is selected
            self.present(imageSelector, animated: true, completion: nil) // Present the imageSelector
        }
    }
    // Text Recognition
    // Create request
    var request = VNRecognizeTextRequest(completionHandler: nil)
    private func VisionOCR(image: UIImage?){
        var textString = "" // Variable textString = string
        // Create completion handler
        request = VNRecognizeTextRequest(completionHandler: { (request, error)in  // Locates all the text in the image.
            // Results are in the request
            guard let results = request.results as?[VNRecognizedTextObservation] else {fatalError("Recieved Invalid Observation")}
            for visionResult in results{
                guard let recognisedText = visionResult.topCandidates(1).first else{ // Text stored in chronological order.
                    print("No text")
                    continue
                }
                textString += "\n\(recognisedText.string)"
                DispatchQueue.main.async{ // FIFO queue
                    self.stopAnimating() // Hide the activityIndicator
                    self.textView.text = textString // Assign the textView the recoginsed text
                }
            }
        })
        // Properties
        request.minimumTextHeight = 0.03125 // Default mimnimum height for text to be recognised in comparison to image, (1/32).
        request.recognitionLevel = .accurate // Choice between accurate and fast.
        request.recognitionLanguages = ["en_UK", "en-US", "fr-FR", "it-IT", "de-DE", "es-ES", "pt-BR", "zh-Hans", "zh-Hant", "yue-Hans", "yue-Hant", "ko-KR", "ja-JP", "ru-RU", "uk-UA"] // Recognisable languages.
        request.usesLanguageCorrection = true // Applies language correction.
        let requests = [request]
![]("https://developer.apple.com/forums/content/attachment/802b997e-3c1d-400a-ad6a-a914a23599f7" "title=Simulator Screen Shot - iPhone 14 Pro Max - 2022-12-13 at 12.39.54.png;width=1290;height=2796")

        // MARK: - Pop-up Button
        let AccurateRecognition = { (action: UIAction) in
            print(action.title) // do color change job
            self.request.recognitionLevel = VNRequestTextRecognitionLevel.fast
        }
        let FastRecognition = { (action: UIAction) in
            print(action.title) // do color change job
            self.request.recognitionLevel = VNRequestTextRecognitionLevel.fast
        }
        lazy var popupButton: UIButton = {
            let button = UIButton(primaryAction: nil)
            button.menu = UIMenu(children: [
                UIAction(title: "Accurate", handler: AccurateRecognition),
                UIAction(title: "Fast", state: .on, handler: FastRecognition),
            ])
            button.showsMenuAsPrimaryAction = true
            button.changesSelectionAsPrimaryAction = true // now its a pop-up button
            return button
        }()
         func accessPopupButtonProperties() {
            // Update to the currently set one
            print(popupButton.menu?.selectedElements.first?.title ?? "") // get currenty set
            // Update the selection
            (popupButton.menu?.children[0] as? UIAction)?.state = .on
        }
        // Request handler
        DispatchQueue.global(qos: .userInitiated).async{  // FIFO queue, qos (quality of service) determines priotity for scheduling tasks
            guard let img = image?.cgImage else {fatalError("Missing image to scan")} // Variable image = computer generated image
            // Create request handler
            let requestHandler = VNImageRequestHandler(cgImage: img, options: [:])  // Performs Vision requests
            // Send request to request handler
            try? requestHandler.perform(requests) // Schedules Vision requests to be performed
        }   
    }
}
extension ViewController2: UIImagePickerControllerDelegate, UINavigationControllerDelegate{
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey :Any]){
        picker.dismiss(animated: true, completion: nil)
        startAnimating()
        self.textView.text = ""
        let image = info[UIImagePickerController.InfoKey.originalImage]as?UIImage
        self.imageView.image = image
        VisionOCR(image: image)
    }
}
public struct storyboardview2: UIViewControllerRepresentable{
    public func makeUIViewController(context content: Context) -> UIViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let controller = storyboard.instantiateViewController(identifier: "selectPhoto")
        return controller
    }
    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    }
}

OK, then you can do something very simple.

Use a segmentedControl with 2 items.

You may have an "Accuracy" button which sole action will be to show the segmentedControl. Once selection done, hide the segmentedControl.

In the image below, you can see that I have a file called SettingsTableViewCell which tries to access a variable called request.recognitionlevel.

In the image below, you can see that I have another file called ViewController2, which defines and uses the variable request.recognition level.

How can I make the variable request.recognition visible in the SettingsTableViewCell, so that's its value in ViewController2 can be changed from the button in SettingsTableView?

If I understand, you want to change a var defined in ViewController4 from ViewController2.

You have several options:

  • define a global var request. Not very clean.
  • if ViewController 2 and 4 are loaded, post a notification from controller4 when you change request. And have controller2 listen to such notification to change the accuracy. Problem if VC4 is not loaded when you post: notification may be lost.
  • Use delegation. That is the cleanest way.

In the old post I desxcribed the mechanism if you need it: https://forums.developer.apple.com/thread/111569

It is speaking of textFields, but you can easily apply to accuracy:


You have VC1 and VC2 ; you want to update a field in VC1 when you change a text in VC2.

Declare a protocol protocol UpdateVC1 { func updateName(newName: String) }

Declare that VC1 conforms to it, and implement it: Set the delegate property of VC2 when you segue class VC1: UIViewController, UpdateVC1 { @IBOutlet weak var userNameField: UITextField!

func updateName(newName: String) {
    userNameField.text = newName
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? VC2 {
        destination.delegate = self
    }
}

}

In VC2 declare a delegate property: class VC2: UIViewController {

var delegate: UpdateVC1?

Then, when you change the field with name, invoke the delegate

    delegate?.updateName(newName: name)

So, I have added the code shown below in my ViewController2, which defines the variable.

        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if let destination = segue.destination as? ViewController4 {
                destination.delegate = self
            }
        }
        func updatetoAccurate(newName: String) {
            request.recognitionLevel = .accurate // Choice between accurate and fast.
        }
        override func prepare2(for segue: UIStoryboardSegue, sender: Any?) {
            if let destination = segue.destination as? ViewController4 {
                destination.delegate = self
            }
        }

And I have added the code below in my ViewController4, which attempts to change the value of the variable:

    let AccurateRecognition = { (action: UIAction) in
        print("Accurate")
        print(action.title) // do color change job
        //request.recognitionLevel = .accurate // Choice between accurate and fast.
        var delegate: UpdateViewController2?
        delegate?.updatetoFast(newName: name)
    }
    let FastRecognition = { (action: UIAction) in
        print("Fast")
        print(action.title) // do color change job
        //request.recognitionLevel = .fast // Choice between accurate and fast.
        var delegate: UpdateViewController2?
        delegate?.updatetoAccurate(newName: name)
    }

However, I still receive multiple errors as shown in the photos below.

ViewController2:

ViewController4:

Waouh, I think you got confused by my explanation when you tried to apply.

The principle is:

  • You declare a protocol
protocol UpdateVC4 {
   func updateAccuracy(newAcc: VNRequestTextRecognitionLevel)
}
  • Declare that VC4 conforms to it,
class ViewController4 : UIViewController, UpdateVC4 {
  • and implement the protocol in VC4:
func updateAccuracy(newAcc: VNRequestTextRecognitionLevel) {
    // Set accuracy value here
}
  • In VC2 declare a delegate property:
class ViewController2: UIViewController {
    var delegate: UpdateVC4?
  • Then, when you change the accuracy in VC2 with newAccuracyValue, invoke the delegate
    delegate?.updateAccuracy(newAcc: newAccuracyValue)  // Note: use the right var, newAccuracyValue is just a name I invented here
  • When you transition from VC4 to VC2, set the delegate property of VC2
  • do you use a segue ? if so, you do it in prepare
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? VC2 {
        destination.delegate = self
    }
}

If that doesn't work or you miss something, please post the complete code of ViewController2 and ViewController4 so that we can tell exactly how to implement.

Below I have attached the code for my ViewController4 and a screenshot of the error.

Any thought in how to fix this would be great.

import UIKit
import SwiftUI
class ViewController4: UIViewController, UpdateVC4 {
    let stackView: UIStackView = {
        let stack = UIStackView()
        stack.axis = .vertical
        stack.alignment = .center
        stack.spacing = 20
        return stack
    }()

    protocol UpdateVC4 {
       func updateAccuracy(newAcc: VNRequestTextRecognitionLevel)
    }

    func updateAccuracy(newAcc: VNRequestTextRecognitionLevel) {
        // Set accuracy value here
        request.recognitionLevel = .fast // Choice between accurate and fast.
    }

    lazy public var popupButton: UIButton = {
        let button = UIButton(primaryAction: nil)
        button.menu = UIMenu(children: [
            UIAction(title: "Accurate Recignition", state: .on, handler: updateAccuracy),
            UIAction(title: "Fast Recognition",handler: updateAccuracy),
        ])
        button.showsMenuAsPrimaryAction = true
        button.changesSelectionAsPrimaryAction = true // now its a pop-up button
        return button
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }
    fileprivate func accessPopupButtonProperties() {
        // Update to the currently set one
        print(popupButton.menu?.selectedElements.first?.title ?? "") // get currenty set
        // Update the selection
        (popupButton.menu?.children[0] as? UIAction)?.state = .on
    }
    fileprivate func setupUI() {
        view.backgroundColor = .systemBackground
        stackView.translatesAutoresizingMaskIntoConstraints = false
        popupButton.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        stackView.addArrangedSubview(popupButton)
        NSLayoutConstraint.activate([
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
            stackView.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor, constant: 100),
            stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -100),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }
}
public struct storyboardview4: UIViewControllerRepresentable{
    public func makeUIViewController(context content: Context) -> UIViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let controller = storyboard.instantiateViewController(identifier: "Settings")
        return controller
    }
    public func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    }
}

Toggle
 
 
Q