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.