2

I am experiencing an issue when deep linking into a certain SwiftUI view from a link. the openTakeVC is what is called when deep linked. Currently it was to be embedded in a UINavigationController in order to work, if I try just presenting the UIHostingController I get a crash with this error:

Thread 1: "Application tried to present modally a view controller <_TtGC7SwiftUI19UIHostingControllerV8uSTADIUM8TakeView_: 0x14680a000> that has a parent view controller <UINavigationController: 0x1461af000>."

The dismiss functionality works perfectly fine if not embedded in a UINavigationController but I am only able to deep link that view using a UINavigationController.

Is there a fix for this error or a way to dismiss a UIHostingController embedded in a UINavigationController?

func openTakeVC(take: TakeOBJ) {
            DispatchQueue.main.async {
                
                guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
                if let _ = appDelegate.window?.rootViewController as? BannedViewController { return }
                
                //let vc = TakeSingleViewController(nibName: "TakeSingleView", bundle: nil, take: take)
                let vc = UIHostingController(rootView: TakeView(take: take))
                let nav = UINavigationController(rootViewController: vc)
                nav.modalPresentationStyle = .fullScreen
                nav.setNavigationBarHidden(true, animated: false)
                
                appDelegate.window?.rootViewController?.present(vc, animated: true, completion: nil)
                UserDefaults.removeURLToContinue()
            }
        }

in TakeView

@Environment(\.presentationMode) var presentationMode


  Button {
     UIImpactFeedbackGenerator(style: .light).impactOccurred()
     presentationMode.wrappedValue.dismiss()
                        
     } label: {
        Image(systemName: "xmark")
     }
  }

2 Answers 2

0

What about creating the hostingcontroller as a separate vc and dismissing it from there?

TakeSingleVC:

final class TakeSingleVC: UIViewController {
    
    var viewModel: TakeViewModel
    var subscriptions = Set<AnyCancellable>()
    
    init(viewModel: TakeViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let childView = UIHostingController(rootView: TakeView(viewModel: viewModel))
        addChild(childView)
        childView.view.frame = view.bounds
        view.addSubview(childView.view)
        childView.didMove(toParent: self)
        
        viewModel.dismissSheet
            .sink { isDismissed in
                if isDismissed {
                    childView.dismiss(animated: true)
                }
            }.store(in: &subscriptions)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Dismissing in TakeView

viewModel.dismissSheet.send(true)

TakeViewModel:

final class TakeViewModel: ObservableObject {
    
    // UIKit
    var dismissSheet = CurrentValueSubject<Bool, Never>(false)
}

and then change your presentation to

let vc = TakeSingleVC(viewModel: viewModel)
let nav = UINavigationController(rootViewController: vc)
Sign up to request clarification or add additional context in comments.

Comments

0

The dismiss functionality works perfectly fine if not embedded in a UINavigationController but I am only able to deep link that view using a UINavigationController.

Is there a fix for this error or a way to dismiss a UIHostingController embedded in a UINavigationController?

SwiftUI DismissAction available via @Environment(.dismiss) won't work with UIHostingController if the UIHostingController is part of a UINavigationController stack, even if the SwiftUI button is available in your navigation bar.

This is because DismissAction works contextually

You can use this action to: Dismiss a modal presentation, like a sheet or a popover. Pop the current view from a NavigationStack. Close a window that you create with WindowGroup or Window. The specific behavior of the action depends on where you call it from.

https://developer.apple.com/documentation/SwiftUI/DismissAction

When inside a UIHostingController that is inside a UINavigationController, the dismiss action (if the UIHostingController is the only item in navigation stack) will try to pop from the navigation stack instead of dismissing.

You can hack your way around it by having your own custom subclass for UIHostingController, but I would not recommend that path. However such an approach has its caveats, so won't be posting it in the answer here.

Much safer approach would be passing into the SwiftUI view, your own dismiss action via a closure.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.