Dismiss SwiftUI View back to a UIKit ViewController - ios

Would like to return to my presenting UIKit View Controller after pressing the save button on my SwiftUI View (presented via a HostingViewController).
This is how a navigate to my SwiftUI view from my UIKit VC.
let profileView = suiProfileView().environmentObject(suiProfileViewModel())
let profileVC = UIHostingController(rootView: profileView)
let navVC = UINavigationController(rootViewController: profileVC)
navVC.modalPresentationStyle = .fullScreen
SideMenuManager.default.leftMenuNavigationController?.present(navVC, animated: true , completion: nil)
This is my SwiftUI View that i'd like to dismiss after pressing the Save button
import SwiftUI
import Combine
struct suiProfileView: View {
#SwiftUI.Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
#EnvironmentObject var profileViewModel: suiProfileViewModel
#State var suiTitleText = t("general.save")
var body: some View {
GeometryReader { geo in
VStack {
personalInfoSection
Spacer()
Button("Save", action: profileViewModel.saveProfile)
.frame(width: geo.size.width * 0.8, height: geo.size.height * 0.08, alignment: .center)
}
.onReceive(profileViewModel.viewDismissalModePublisher) { shouldDismiss in
if shouldDismiss {
self.presentationMode.wrappedValue.dismiss()
}
}
.frame(width: geo.size.width, height: geo.size.height, alignment: .center)
.padding()
}
}
}
This is my ProfileViewModel class which publishes the shouldDismissView variable using Combine after running some business logic.
class suiProfileViewModel: suiProfileViewModelProtocol {
//private var model: suiProfileFormProtocol
#Published var profile = suiProfileForm()
var viewDismissalModePublisher = PassthroughSubject<Bool, Never>()
private var shouldDismissView = false {
didSet {
viewDismissalModePublisher.send(shouldDismissView)
}
}
func businessLogicThatDeterminesIfShouldDismissView() {
//....
}
}
For some reason self.presentationMode.wrappedValue.dismiss() which was called in my SwiftUI view is not dismissing my SwiftUI view and not allowing me to go back to my initial UIKit View Controller who presented it in the first place. Any help would greatly appreciated. Thanks in advance

A couple problems that I see:
You're trying to dismiss your suiProfileView , but really, it's wrapped in a UINavigationController before you present it.
According to a basic test that I just did, even if your suiProfileView were presented without the navigation controller, presentationMode still doesn't work on it -- my suspicion is that it's only passed accurately when going SwiftUI->SwiftUI and can't be trusted to be communicated through the UIHostingController
I would suggest passing a closure to suiProfileView that can run the dismiss code from your original presenting view controller. Here's a simplified example:
import UIKit
import SwiftUI
class ViewController: UIViewController {
var presentedController : UINavigationController?
func dismissVC() {
presentedController?.dismiss(animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let swiftUIView = ContentView(dismiss: self.dismissVC)
let hostVC = UIHostingController(rootView: swiftUIView)
let navVC = UINavigationController(rootViewController: hostVC)
navVC.modalPresentationStyle = .fullScreen
self.present(navVC, animated: true , completion: nil)
self.presentedController = navVC
}
}
}
struct ContentView : View {
var dismiss : () -> Void
#Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
var body: some View {
Button(action: {
dismiss()
//presentationMode.wrappedValue.dismiss() <-- doesn't function
}) {
Text("Close")
}
}
}

Related

SwiftUI, Dismiss modal/page from child view

I am new to Swift/SwiftUI and want to know how to dismiss modal/page from nested child view.
Firstly, I am calling from Flutter, UIHostingController, then SwiftUI page. (currently showing as modal...)
After Navigating to SwiftUI, I am not able to use #environment data from child view.
Is there any ways for this to work?
thanks in advance.
AppDelegate.swift
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = self.window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel.init(name: "com.example.show", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler({
(call, result) -> Void in
if call.method == "sample" {
let vc = UIHostingController(rootView: ContentView())
vc.modalPresentationStyle = .fullScreen
controller.present(vc, animated: true,completion: nil)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ContentView.swift
struct ContentView: View{
#Environment(\.presentationMode) var presentation: Binding<PresentationMode>
private var childView: ChildView()
var body: some View{
NavigationView{
ZStack{
childView
Button(action: {
// This works ************************
self.presentation.wrappedValue.dismiss()
}, label: {
Text("close")
})
}
}
}
}
ChildView.swift
struct ChildView: View{
#Environment(\.presentationMode) var presentation: Binding<PresentationMode>
var body: some View{
Button(action: {
// This won't do anything *********************************
self.presentation.wrappedValue.dismiss()
// nor this↓ **********************************************
if #available(iOS 15.0, *) {
#Environment(\.dismiss) var dismiss;
dismiss()
dismiss.callAsFunction()
}
}, label: {
Text("close")
})
}
}
Since you had another NavigationView is your ContentView, the #Environment(\.presentation) inside ChildView is of a child and not the parent. Basically those two are from completely different Navigation stacks.
In order to still keep NavigationView inside your parent ContentView, you need to pass the presentation value from constructor of ChildView instead of environment:
ContentView.swift
struct ContentView: View{
#Environment(\.presentationMode) var presentation: Binding<PresentationMode>
var body: some View{
NavigationView{
ZStack{
ChildView(parentPresentation: presentation)
Button(action: {
self.presentation.wrappedValue.dismiss()
}, label: {
Text("close")
})
}
}
}
}
In child view, use normal property instead of #Environment
ChildView.swift
struct ChildView: View{
let parentPresentation: Binding<PresentationMode>
var body: some View{
Button(action: {
self.parentPresentation.wrappedValue.dismiss()
if #available(iOS 15.0, *) {
#Environment(\.dismiss) var dismiss;
dismiss()
dismiss.callAsFunction()
}
}, label: {
Text("Close")
})
}
}
For iOS 15.0 and above, we can use the new environment value dismiss, and for that to work with child view, we should also pass it from the parent view to the child view:
ContentView.swift
struct ContentView: View {
#Environment(\.dismiss) private var dismiss
var body: some View {
NavigationView {
ZStack {
ChildView(parentDismiss: dismiss)
Button {
dismiss()
} label: {
Text("close")
}
}
}
}
}
ChildView.swift
struct ChildView: View {
let parentDismiss: DismissAction
var body: some View {
Button {
parentDismiss()
} label: {
Text("Close")
}
}
}
I figured it out that NavigationView in ContentView.swift caused this issue.
Removing NavigationView, I could dismiss modal page from child view...
But this is not what I intended for :(...
var body: some View{
// NavigationView{ <--------
ZStack{
childView
Button(action: {
self.presentation.wrappedValue.dismiss()
}, label: {
Text("close")
})
}
// }
}

How do I call a function written in UIKit/UIController/Storyboard from SwiftUI button

I am a SwiftUI newbie struggling to add SwiftUI functionality to my existing UIKit/Storyboard code. I would like to call a UIKit function from SwiftUI button. Greatly appreciate your help. Here is the relevant code simplified for this discussion.
From the code below, I'd like to call the functions startAction() and stopAction() from the If statement in SwiftUI...
if (startStop_flag) {
//******** call StartAction()
} else {
//******* call StopAction()
}
The entire code below.
Some context: when the app is run, the bottom half of the screen will show "UIkit Storyboard View" and show the button "Open Swift Container View". When the user clicks this button, the SwiftUI container view will open up. This view will display "This is a swiftUI view" and display a button "Start/Stop". When the user clicks this button, StartAction() or StopAction() needs to be called. These two functions reside in the UIViewController. I hope I am clear with the problem and the request.
ViewController.swift
class ViewController: UIViewController {
#IBOutlet weak var nativeView: UIView!
#IBOutlet weak var nativeView_Label: UILabel!
#IBOutlet weak var nativeView_openSwiftViewBtn: UIButton!
#IBOutlet weak var containerView_forSwiftUI: UIView!
#IBSegueAction func embedSwiftUIView(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: SwiftUIView2(text: "Container View"))
}
var toggleOpenCloseContainerView : Bool = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
containerView_forSwiftUI.isHidden = true
}
#IBAction func openContainerView_touchInside(_ sender: Any) {
if (toggleOpenCloseContainerView) {
containerView_forSwiftUI.isHidden = false
toggleOpenCloseContainerView = false
nativeView_openSwiftViewBtn.setTitle("Close Swift Container View", for: .normal)
} else {
containerView_forSwiftUI.isHidden = true
toggleOpenCloseContainerView = true
nativeView_openSwiftViewBtn.setTitle("Open Swift Container View", for: .normal)
}
}
// These two functions need to be called from the SwiftUI's button.
func startAction() {
print("Start Action called from SwiftUI's button")
}
func stopAction() {
print("Stop Action called from SwiftUI's button")
}
}
The swiftUI functions are in this file
struct SwiftUIView2: View {
var text: String
#State var startStop_flag: Bool = true
var body: some View {
VStack {
Text(text)
HStack {
Image(systemName: "smiley")
Text("This is a SwiftUI View")
Spacer()
Button("\(startStop_flag ? "Start": "Stop")") {
startStop_flag = !startStop_flag
if (startStop_flag) {
//******** call StartAction()
} else {
//******* call StopAction()
}
} .padding()
.background(Color.red)
.cornerRadius(40)
.foregroundColor(.white)
.padding(5)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.red, lineWidth: 1)
)
}
}
.font(.largeTitle)
.background(Color.blue)
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView2(text: "Sample Text")
}
}
You can use closures for this. First, define and call them inside your SwiftUI view.
struct SwiftUIView2: View {
var text: String
var startAction: (() -> Void) /// define closure 1
var stopAction: (() -> Void) /// define closure 2
...
...
Button("\(startStop_flag ? "Start": "Stop")") {
startStop_flag = !startStop_flag
if (startStop_flag) {
//******** call StartAction()
startAction()
} else {
//******* call StopAction()
stopAction()
}
}
}
Then, just assign the closure's contents inside ViewController.swift.
#IBSegueAction func embedSwiftUIView(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(
coder: coder,
rootView: SwiftUIView2(
text: "Container View",
startAction: { [weak self] in
self?.startAction()
},
stopAction: { [weak self] in
self?.stopAction()
}
)
)
}
Here is the easiest way to open UIkit ViewController from SwiftUI Button Press.
Button("Next Page") {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootViewController = storyboard.instantiateViewController(withIdentifier: "ProfileVC")
if let window = UIApplication.shared.windows.first {
window.rootViewController!.present(rootViewController, animated: true)
}
}
}

How do I show a SwiftUI Subview fullscreen?

Take a look at the screenshot:
I marked the "Top Navigation Bar" red, which I want to remove, as there is an unused top bar...
You have to know that I code using Storyboards, but this specific page is holding a subview of SwiftUI View!
This is the SwiftUI ContentView:
import SwiftUI
import UIKit
struct ContentView: View {
var body: some View {
NavigationView{
MasterView()
}.navigationViewStyle(DoubleColumnNavigationViewStyle())
}
}
struct MasterView: View {
var body: some View {
Form {
Section(header: Text("Geplant")) {
Section {
NavigationLink(destination: UIKitView()) { Text("Berlin") }
}
}
}
.navigationBarTitle("Wohin gehts?")
}
}
struct UIKitView: UIViewControllerRepresentable {
typealias UIViewControllerType = SwipeViewController
func makeUIViewController(context: Context) -> SwipeViewController {
let sb = UIStoryboard(name: "Storyboard", bundle: nil)
let viewController = sb.instantiateViewController(identifier: "swipe") as! SwipeViewController
return viewController
}
func updateUIViewController(_ uiViewController: SwipeViewController, context: Context) {
}
}
And this is the UIViewController, which is holding the SwiftUI Subview:
import UIKit
import SwiftUI
class StartViewController: UIViewController {
#IBOutlet weak var btn: UIButton!
#IBOutlet weak var container: UIView!
let contentView = UIHostingController(rootView: ContentView())
override func viewDidLoad() {
super.viewDidLoad()
configureBackgroundGradient()
addChild(contentView)
view.addSubview(contentView.view)
setupContraints()
}
fileprivate func setupContraints(){
contentView.view.translatesAutoresizingMaskIntoConstraints = false
contentView.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
contentView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
contentView.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
contentView.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
private func configureBackgroundGradient() {
let backgroundGray = UIColor(red: 244 / 255, green: 247 / 255, blue: 250 / 255, alpha: 1)
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.white.cgColor, backgroundGray.cgColor]
gradientLayer.frame = view.bounds
view.layer.insertSublayer(gradientLayer, at: 0) //Background Color
}
}
Can anyone can help? :))
Thank you! Feel free to ask me for more screenshots or code!
You can show a view in full screen with the SwiftUI view modifiere fullScreenCover. https://www.hackingwithswift.com/quick-start/swiftui/how-to-present-a-full-screen-modal-view-using-fullscreencover
Let us take simple FullScreenModalView struct that can dismiss itself, then presents it from ContentView when another button is pressed:
struct FullScreenModalView: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
Button("Dismiss Modal") {
presentationMode.wrappedValue.dismiss()
}
}
}
And here is the code for ContentView -
struct ContentView: View {
#State private var isPresented = false
var body: some View {
Button("Present!") {
isPresented.toggle()
}
.fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)
}
}
Happy to help.
Thanks.

Hide StatusBar And NavigationBar On MainView But Show It On DetailView In Swift

I've written two SwiftUI Views like below. In here, I want to;
Hide statusbar and navigation bar on mainview
Show StatusBar and NavigationBar on DetailView
But when I implement it like below, somethings go wrong.
FirstView.swift
import SwiftUI
struct FirstView: View {
var body: some View {
NavigationView {
NavigationLink(destination: SecondView()) {
Text("Main View")
}
}
.edgesIgnoringSafeArea(.all)
.navigationBarHidden(true)
.statusBar(hidden: true)
}
}
SecondView.swift
import SwiftUI
import UIKit
import WebKit
class SecondViewController: UIViewController, WKUIDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let myURL = URL(string: "https://www.stackoverflow.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
override func loadView() {
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
view = webView
}
}
private struct SecondViewHolder: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> SecondViewController {
return SecondViewController()
}
func updateUIViewController(_ uiViewController: SecondViewController, context: Context) {
}
}
struct SecondView: View {
var body: some View {
VStack(spacing: 0) {
HeaderView()
SecondViewHolder()
FooterView()
}
.edgesIgnoringSafeArea(.all)
.navigationBarHidden(false)
.statusBar(hidden: true)
}
}
struct HeaderView: View {
var body: some View {
Text("Header Note")
.foregroundColor(.yellow)
.frame(maxWidth: .infinity, minHeight: 60)
.background(Color.red)
}
}
struct FooterView: View {
var body: some View {
Text("Footer Note")
.foregroundColor(.yellow)
.frame(maxWidth: .infinity, minHeight: 100)
.background(Color.red)
}
}
When I run it on IOS simulator:
Views look so wrong. In second page I expect a NavigationBar and Header. But I couldn't see them. I am so new at Swift. Could you please help me about it?
Try the following
struct FirstView: View {
#State private var noBars = true
var body: some View {
NavigationView {
NavigationLink(destination:
SecondView()
.onAppear { self.noBars = false }
.onDisappear { self.noBars = true }
) {
Text("Main View")
}
}
.edgesIgnoringSafeArea(.all)
.navigationBarHidden(noBars)
.statusBar(hidden: noBars)
}
}

How to give back swipe gesture in SwiftUI the same behaviour as in UIKit (interactivePopGestureRecognizer)

The interactive pop gesture recognizer should allow the user to go back the the previous view in navigation stack when they swipe further than half the screen (or something around those lines). In SwiftUI the gesture doesn't get canceled when the swipe wasn't far enough.
SwiftUI: https://imgur.com/xxVnhY7
UIKit: https://imgur.com/f6WBUne
Question:
Is it possible to get the UIKit behaviour while using SwiftUI views?
Attempts
I tried to embed a UIHostingController inside a UINavigationController but that gives the exact same behaviour as NavigationView.
struct ContentView: View {
var body: some View {
UIKitNavigationView {
VStack {
NavigationLink(destination: Text("Detail")) {
Text("SwiftUI")
}
}.navigationBarTitle("SwiftUI", displayMode: .inline)
}.edgesIgnoringSafeArea(.top)
}
}
struct UIKitNavigationView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(#ViewBuilder content: #escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UINavigationController {
let host = UIHostingController(rootView: content())
let nvc = UINavigationController(rootViewController: host)
return nvc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
I ended up overriding the default NavigationView and NavigationLink to get the desired behaviour. This seems so simple that I must be overlooking something that the default SwiftUI views do?
NavigationView
I wrap a UINavigationController in a super simple UIViewControllerRepresentable that gives the UINavigationController to the SwiftUI content view as an environmentObject. This means the NavigationLink can later grab that as long as it's in the same navigation controller (presented view controllers don't receive the environmentObjects) which is exactly what we want.
Note: The NavigationView needs .edgesIgnoringSafeArea(.top) and I don't know how to set that in the struct itself yet. See example if your nvc cuts off at the top.
struct NavigationView<Content: View>: UIViewControllerRepresentable {
var content: () -> Content
init(#ViewBuilder content: #escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UINavigationController {
let nvc = UINavigationController()
let host = UIHostingController(rootView: content().environmentObject(nvc))
nvc.viewControllers = [host]
return nvc
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {}
}
extension UINavigationController: ObservableObject {}
NavigationLink
I create a custom NavigationLink that accesses the environments UINavigationController to push a UIHostingController hosting the next view.
Note: I didn't implement the selection and isActive that the SwiftUI.NavigationLink has because I don't fully understand what they do yet. If you want to help with that please comment/edit.
struct NavigationLink<Destination: View, Label:View>: View {
var destination: Destination
var label: () -> Label
public init(destination: Destination, #ViewBuilder label: #escaping () -> Label) {
self.destination = destination
self.label = label
}
/// If this crashes, make sure you wrapped the NavigationLink in a NavigationView
#EnvironmentObject var nvc: UINavigationController
var body: some View {
Button(action: {
let rootView = self.destination.environmentObject(self.nvc)
let hosted = UIHostingController(rootView: rootView)
self.nvc.pushViewController(hosted, animated: true)
}, label: label)
}
}
This solves the back swipe not working correctly on SwiftUI and because I use the names NavigationView and NavigationLink my entire project switched to these immediately.
Example
In the example I show modal presentation too.
struct ContentView: View {
#State var isPresented = false
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.isPresented.toggle()
}, label: {
Text("Show modal")
})
}
.navigationBarTitle("SwiftUI")
}
.edgesIgnoringSafeArea(.top)
.sheet(isPresented: $isPresented) {
Modal()
}
}
}
struct Modal: View {
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
VStack(alignment: .center, spacing: 30) {
NavigationLink(destination: Text("Detail"), label: {
Text("Show detail")
})
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Dismiss modal")
})
}
.navigationBarTitle("Modal")
}
}
}
Edit: I started off with "This seems so simple that I must be overlooking something" and I think I found it. This doesn't seem to transfer EnvironmentObjects to the next view. I don't know how the default NavigationLink does that so for now I manually send objects on to the next view where I need them.
NavigationLink(destination: Text("Detail").environmentObject(objectToSendOnToTheNextView)) {
Text("Show detail")
}
Edit 2:
This exposes the navigation controller to all views inside NavigationView by doing #EnvironmentObject var nvc: UINavigationController. The way to fix this is making the environmentObject we use to manage navigation a fileprivate class. I fixed this in the gist: https://gist.github.com/Amzd/67bfd4b8e41ec3f179486e13e9892eeb
You can do this by descending into UIKit and using your own UINavigationController.
First create a SwipeNavigationController file:
import UIKit
import SwiftUI
final class SwipeNavigationController: UINavigationController {
// MARK: - Lifecycle
override init(rootViewController: UIViewController) {
super.init(rootViewController: rootViewController)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
delegate = self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
// This needs to be in here, not in init
interactivePopGestureRecognizer?.delegate = self
}
deinit {
delegate = nil
interactivePopGestureRecognizer?.delegate = nil
}
// MARK: - Overrides
override func pushViewController(_ viewController: UIViewController, animated: Bool) {
duringPushAnimation = true
super.pushViewController(viewController, animated: animated)
}
var duringPushAnimation = false
// MARK: - Custom Functions
func pushSwipeBackView<Content>(_ content: Content) where Content: View {
let hostingController = SwipeBackHostingController(rootView: content)
self.delegate = hostingController
self.pushViewController(hostingController, animated: true)
}
}
// MARK: - UINavigationControllerDelegate
extension SwipeNavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.duringPushAnimation = false
}
}
// MARK: - UIGestureRecognizerDelegate
extension SwipeNavigationController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard gestureRecognizer == interactivePopGestureRecognizer else {
return true // default value
}
// Disable pop gesture in two situations:
// 1) when the pop animation is in progress
// 2) when user swipes quickly a couple of times and animations don't have time to be performed
let result = viewControllers.count > 1 && duringPushAnimation == false
return result
}
}
This is the same SwipeNavigationController provided here, with the addition of the pushSwipeBackView() function.
This function requires a SwipeBackHostingController which we define as
import SwiftUI
class SwipeBackHostingController<Content: View>: UIHostingController<Content>, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.duringPushAnimation = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
swipeNavigationController.delegate = nil
}
}
We then set up the app's SceneDelegate to use the SwipeNavigationController:
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let hostingController = UIHostingController(rootView: ContentView())
window.rootViewController = SwipeNavigationController(rootViewController: hostingController)
self.window = window
window.makeKeyAndVisible()
}
Finally use it in your ContentView:
struct ContentView: View {
func navController() -> SwipeNavigationController {
return UIApplication.shared.windows[0].rootViewController! as! SwipeNavigationController
}
var body: some View {
VStack {
Text("SwiftUI")
.onTapGesture {
self.navController().pushSwipeBackView(Text("Detail"))
}
}.onAppear {
self.navController().navigationBar.topItem?.title = "Swift UI"
}.edgesIgnoringSafeArea(.top)
}
}

Resources