To apply custom transition animations, I implemented a UIViewControllerRepresentable
wrapping UINavigationController
instead of using NavigationStack
.
The navigation stack pops correctly with DismissAction
.
However, when I call DismissAction
after presenting my CustomNavigationStack
with fullScreenCover(isPresented:onDismiss:content:)
, it doesn't work.
I suspect that the DismissAction
implementation does not handle this case. Is there a workaround for this?
Below is a simple reproduction code:
struct ChildView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button {
dismiss()
} label: {
Text(verbatim: "Dismiss!!")
.background(Color.yellow)
}
}
}
struct CustomSimpleVC: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIHostingController<ChildView> {
let vc = UIHostingController(rootView: ChildView())
vc.view.backgroundColor = .green
return vc
}
func updateUIViewController(_ uiViewController: UIHostingController<ChildView>, context: Context) {}
}
struct CustomContainerVC: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let childVC = UIHostingController(rootView: ChildView())
childVC.view.backgroundColor = .blue
let vc = UIViewController()
vc.addChild(childVC)
vc.view.addSubview(childVC.view)
childVC.view.frame = vc.view.bounds
childVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
childVC.didMove(toParent: vc)
return vc
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct CustomNavigationVC: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UINavigationController {
let childVC = UIHostingController(rootView: ChildView())
childVC.view.backgroundColor = .red
let childVC2 = UIHostingController(rootView: ChildView())
childVC2.view.backgroundColor = .gray
let navVC = UINavigationController()
navVC.viewControllers = [childVC, childVC2]
return navVC
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
struct ContentView: View {
@State private var isNextPresented: Bool = false
var body: some View {
VStack {
Button {
isNextPresented = true
} label: {
Text(verbatim: "present")
}
}
.fullScreenCover(isPresented: $isNextPresented, content: {
VStack {
CustomSimpleVC() // Works
CustomContainerVC() // Works
CustomNavigationVC() // Does not work
}
})
}
}
#Preview {
ContentView()
}
You need a way to communicate the dismissal action from within the SwiftUI view to the UIKit NavigationController by passing down the isNextPresented
binding down to the child views.
struct ChildView: View {
@Binding var isPresented: Bool
var body: some View {
Button {
isPresented = false
} label: {
Text("Dismiss!!")
.background(Color.yellow)
}
}
}
struct ContentView: View {
@State private var isNextPresented: Bool = false
var body: some View {
VStack {
Button {
isNextPresented = true
} label: {
Text(verbatim: "present")
}
}
.fullScreenCover(isPresented: $isNextPresented, content: {
VStack {
...
CustomNavigationVC(isPresented: $isNextPresented)
}
})
}
}
struct CustomNavigationVC: UIViewControllerRepresentable {
@Binding var isPresented: Bool
func makeUIViewController(context: Context) -> UINavigationController {
let childVC = UIHostingController(rootView: ChildView(isPresented: $isPresented))
childVC.view.backgroundColor = .red
let childVC2 = UIHostingController(rootView: ChildView(isPresented: $isPresented))
childVC2.view.backgroundColor = .gray
let navVC = UINavigationController()
navVC.viewControllers = [childVC, childVC2]
return navVC
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}