learning swift and other stuff

Dismissing modal SwiftUI View presented in UIHostingController from UIKit

Published November 2020

A long title trying to explain a complex situation that needs a simple solution.

If you, like me, find yourself in a position where you try to gently introduce some new features (SwiftUI) into your existing world (UIKit) you might come across the same problem i did.

what i want to do

i want to dispay a more complex view from UIKit, that should behave like a modal dialog.

This new view should be done with SwiftUI

I display some information and offer the user an cancel and ok button.

it should look something like this:

Dialog

so to call our new dialog from some UIKit viewcontroller we create a UIHostingController and present it.

let swiftUIController = UIHostingController(
  rootView: pasteLocationsView(
              pasteLocationsViewModel: PasteLocationsViewModel(indexPaths: [indexPath], fetchResult: fetchResult) 
            )
)

self.navigationController?.present(swiftUIController, animated: true, completion: {
    print( "completion")
})

the problem

as the view is modal we need to dismiss it when the user selects either button.

SwiftUI does currently not offer anything to achieve this with a view presented from UIKit in a UIHostingController.

the solution

we send a dismissAction closure to our dialog wich it can call when the user hits either button:

let swiftUIController = UIHostingController(
  rootView: pasteLocationsView(
              pasteLocationsViewModel: PasteLocationsViewModel(indexPaths: [indexPath], fetchResult: fetchResult), 
              dismissAction: {self.dismiss( animated: true, completion: nil )}
            )
)

self.navigationController?.present(swiftUIController, animated: true, completion: {
    print( "completion")
})

On the button actions in pasteLocationsView we call our dismissAction() closure:

... 
/// a "Dialog" asking for permission to do something
///
/// **dismissAction** an optional dismiss Action Closure called when a button is selected
struct pasteLocationsView: View {
...        
    // an optional dismiss Action Closure
    var dismissAction: (() -> Void)?
...
    Button(action: {
        // doSomeStuff
        if let dismissAction = dismissAction {
            dismissAction()
        }    
    }) {
        Text("OK").fontWeight(.bold)
    }
...