In the code below, I am trying to figure out how to execute the savePhoto
function with a SwiftUI
button. This is actually the simplified version of the problem I am trying to solve, but still can't figure this out. The button I created in the ContentView
is how I would imagine to solve the problem, but because of the SwiftUI structs, I can't seem to find a way around it.
Here is my SwiftUI view:
struct ContentView: View {
var body: some View {
ZStack{
AnotherControllerView()
Button(action: { AnotherControllerView.savePhoto() }){
Text("Save Photo")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
And my integrated UIViewController:
import Foundation
import SwiftUI
import UIKit
struct AnotherControllerView : UIViewControllerRepresentable {
typealias UIViewControllerType = AnotherController
func makeCoordinator() -> AnotherControllerView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<AnotherControllerView>) -> AnotherController {
return AnotherController()
}
func updateUIViewController(_ uiViewController: AnotherController, context: UIViewControllerRepresentableContext<AnotherControllerView>) {
}
class Coordinator : NSObject {
var parent : AnotherControllerView
init(_ viewController : AnotherControllerView){
self.parent = viewController
}
}
}
class AnotherController : UIViewController {
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: nil))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: someHandler))
self.present(alert, animated: true)
}
func someHandler(alert: UIAlertAction!) {
print("Handler executed")
}
}
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.
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 you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With