Search code examples
iosswiftuinavigationview

Is there a SwiftUI equivalent for viewWillDisappear(_:) or detect when a view is about to be removed?


In SwiftUI, I'm trying to find a way to detect that a view is about to be removed only when using the default navigationBackButton. Then perform some action.

Using onDisappear(perform:) acts like viewDidDisappear(_:), and the action performs after another view appears.

Or, I was thinking the above problem might be solved by detecting when the default navigationBarBackButton is pressed. But I've found no way to detect that.

Is there any solution to perform some action before another view appears?

(I already know it is possible to do that by creating a custom navigation back button to dismiss a view)


Solution

  • Here is approach that works for me, it is not pure-SwiftUI but I assume worth posting

    Usage:

       SomeView()
       .onDisappear {
            print("x Default disappear")
        }
       .onWillDisappear { // << order does NOT matter
            print(">>> going to disappear")
        }
    

    Code:

    struct WillDisappearHandler: UIViewControllerRepresentable {
        func makeCoordinator() -> WillDisappearHandler.Coordinator {
            Coordinator(onWillDisappear: onWillDisappear)
        }
    
        let onWillDisappear: () -> Void
    
        func makeUIViewController(context: UIViewControllerRepresentableContext<WillDisappearHandler>) -> UIViewController {
            context.coordinator
        }
    
        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<WillDisappearHandler>) {
        }
    
        typealias UIViewControllerType = UIViewController
    
        class Coordinator: UIViewController {
            let onWillDisappear: () -> Void
    
            init(onWillDisappear: @escaping () -> Void) {
                self.onWillDisappear = onWillDisappear
                super.init(nibName: nil, bundle: nil)
            }
    
            required init?(coder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
            }
    
            override func viewWillDisappear(_ animated: Bool) {
                super.viewWillDisappear(animated)
                onWillDisappear()
            }
        }
    }
    
    struct WillDisappearModifier: ViewModifier {
        let callback: () -> Void
    
        func body(content: Content) -> some View {
            content
                .background(WillDisappearHandler(onWillDisappear: callback))
        }
    }
    
    extension View {
        func onWillDisappear(_ perform: @escaping () -> Void) -> some View {
            self.modifier(WillDisappearModifier(callback: perform))
        }
    }