Search code examples
swiftswiftuiuihostingcontroller

Cannot Close SwiftUI View Embedded in UIHostingController


I've read just about every question and answer about closing a View that's loaded in a UIHostingController. They all seem to use some variation on using a button to dismiss the view. In my view the user is uploading an object and on completion I'd like to close the current view and go back to the previous one. The simplest application of this that I could find is here:

https://stackoverflow.com/a/57728357/1321751

..and I've implemented it as follows:

UploadViewController

 required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: UploadView())
        rootView.dismissAction = dismiss
    }
    func dismiss() {
            dismiss(animated: true, completion: nil)
        }

HostingController

struct UploadView: View {
    @State var dismissAction: (() -> Void)?

var body: some View {
        
        VStack {
Button(action: {
                let uiImage: UIImage = self.selectedImage.asUIImage()
                let imageData: Data = uiImage.jpegData(compressionQuality: 0.1) ?? Data()
                 let imageStr: String = imageData.base64EncodedString()
                // let imageStr = String(decoding: imageData, as: UTF8.self)
                
                if let data9 = UserDefaults.standard.data(forKey: "current") {
                    do {
                        // Create JSON Decoder
                        let decoder = JSONDecoder()

                        // Decode Note
                        let current = try decoder.decode(Current.self, from: data9)
                        self.uid = current.id
                        self.uname = current.uname
                        print(current.fid as Any)

                    } catch {
                        print("Unable to Decode User (\(error))")
                    }
                    }
    var urlRequest = URLRequest(url: url)
urlRequest.addDataField(named: remoteName, data: imageData, mimeType: "img/jpeg")
     URLSession.shared.dataTask(with: urlRequest, completionHandler: {
                        (data, response, error) in
                        guard let data = data else {
                            print("Error in Data")
                            return
                        }
    let responseStr: String = String(data: data, encoding: .utf8) ?? ""
    Button("Dimiss", action: dismissAction!)
     }).resume()
                    
                    
                }, label: {
            Text("Upload Image").font(.callout).padding(8).foregroundColor(Color.white).background(Color.black).overlay(
                RoundedRectangle(cornerRadius: 6)
                    .stroke(Color.white, lineWidth: 3)
        )
                
        })
       
    }
    .sheet(isPresented: $showImagePicker, content: {
        ImagePicker(image: self.$selectedImage)
    })
}

This follows the example code pretty much perfectly... but nothing happens. The view doesn't dismiss at all. The operation does complete. The file gets uploaded... Then nothing.

Every other example that I've found of dismissing a view from a UIHostingController has been some variation of this. And I haven't been able to get a single one of them to work.

Any help would be appreciated. OH! And could somebody tell me WHY it's a "button"... instead of just calling upon the "dismiss" action? Why does it HAVE to be a button?


Solution

  • Ok. So a few things in my code needed to be rewritten. First off... the whole "Button" thing was unnecessary:

    HostingController

    //Change:
     let responseStr: String = String(data: data, encoding: .utf8) ?? ""
        Button("Dimiss", action: dismissAction!)
         }).resume()
    
    //To:
     let responseStr: String = String(data: data, encoding: .utf8) ?? ""
        dismissAction!()
         }).resume()
    

    You'll get a purple warning that "dismissAction" needs to be called on the main thread. So back in...

    UploadViewController

    DispatchQueue.main.async {
            
                self.dismiss(animated: true, completion: nil)
                
            }
    

    The part I forgot about was that I was also running a navigation controller. So nothing happened when trying to dismiss the view. I had to thus modify the code:

    DispatchQueue.main.async {
            
               // self.dismiss(animated: true, completion: nil)
                self.navigationController?.popViewController(animated: true)
            }
    

    And that's it. Now it works. (Special thanks to @jnpdx for some helpful advice.

    Hopefully this helps someone who happens to run into this... admittedly unusual circumstance.