How to execute a function in a UIViewController through a SwiftUI Button?

Hello to control the UIViewController we need to create a bind between the View and UIViewControllerRepresentable let explain it in code: first you need to declare a new variable annotated with @Binding inside the AnotherControllerView

it will be like this :

struct AnotherControllerView : UIViewControllerRepresentable {

    @Binding var isShown: Bool
    typealias UIViewControllerType = AnotherController

    func makeCoordinator() -> AnotherControllerView.Coordinator {
        Coordinator()
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController {

        return AnotherController(isShown: $isShown)
    }

    func updateUIViewController(_ controller: AnotherController, context: UIViewControllerRepresentableContext<AnotherControllerView>) {
        if(self.isShown){
            controller.savePhoto()
        }
    }


    class Coordinator : NSObject {

    }
}

so for that in the updateUIViewController we implement the logic there

class AnotherController : UIViewController {

     @Binding var isShown: Bool

    init(isShown: Binding<Bool>) {
        _isShown = isShown
        super.init(nibName: nil, bundle: nil)

    }



    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.blue
    }

    func savePhoto(){

        let alert = UIAlertController(title: "Save Photo to Camera Roll", message: "Would you like to save your drawing to the camera roll?", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: cancelAlert))
        alert.addAction(UIAlertAction(title: "Save", style: .default, handler: someHandler))
        self.present(alert, animated: true)
    }

    func cancelAlert(alert: UIAlertAction!) {
        self.isShown = false
    }

    func someHandler(alert: UIAlertAction!) {
        print("Handler executed")
    }
}

and if you're trying to capture an image let me show you how I implemented this:

struct CaptureImageView {
    @Binding var isShown: Bool
    @Binding var image: Image?
    var sourceType: UIImagePickerController.SourceType

    func makeCoordinator() -> Coordinator {
        return Coordinator(isShown: $isShown, image: $image)
    }
}

extension CaptureImageView: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<CaptureImageView>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = sourceType
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<CaptureImageView>) {

    }

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        @Binding var isCoordindatorShown: Bool
        @Binding var imageInCoordinator: Image?

        init(isShown: Binding<Bool>, image: Binding<Image?>) {
            _isCoordindatorShown = isShown
            _imageInCoordinator = image
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let unwrapImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else {return}
            imageInCoordinator = Image(uiImage: unwrapImage)
            isCoordindatorShown = false
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            isCoordindatorShown = false
        }
    }

}

and in the body I just call :

CaptureImageView(isShown: $showCaptureImageView, image: $imageCaptured, sourceType: importSourceType)

If the AnotherControllerView is large and you use it scarcely in the new, you may consider use a simpler workaround.

        struct ContentAnotherControllerView: View {

            let   anotherControllerView = AnotherControllerView()

            var body: some View {
                ZStack{
                    anotherControllerView
                    Button(action: { self.anotherControllerView.savePhoto() }){
                        Text("Save Photo")
                    }
                }
            }
        }



     struct AnotherControllerView : UIViewControllerRepresentable {

     typealias UIViewControllerType = AnotherController

     var savePhotoDelegate: AnotherController? = AnotherController()

    func savePhoto(){
         savePhotoDelegate?.savePhoto()
     }


     func makeUIViewController(context:   UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController {

    if let savePhotoDelegate = savePhotoDelegate{
        return savePhotoDelegate
    }
    return AnotherController()
    }
   ....}

This approach is just to remind us one view can be reused in some situation. As the UIView bridge is not a pure SwiftUI domain, you can try this way.

Tags:

Ios

Swift

Swiftui