0

I would like to implement a way for keyboard to be dismissed when clicked outside of it. Using this method, with a tap gesture, it's possible to do so.

I applied the gesture to a root view and all is working well, expect for the TabView. Because of the tap gesture, the TabView is not recognizing any inputs when clicking on the tabs.

Using .simultaneousGesture also does not solve the problem.

Everything else, including views inside TabView, is recognizing inputs. Due to the way the app is structured, it's not feasible to apply the .onTapGesture to individual components to avoid the issue and accomplish keyboard dismiss.

Is there a way to solve the issue without using custom tab view, is this just a bug of TabView?

Thanks.

Astro
  • 97
  • 6

1 Answers1

1

This thread has an answer that directly addresses your exact question in a clean way that actually works without interfering with any other controls.

If you combine the top answer with info found in the comments of that answer and by using code provided by Mikhail in his answer on the same thread, you can do it completely in SwiftUI without the need for an App Delegate or Scene Delegate.

Here is the code:

extension UIApplication {
    func addTapGestureRecognizer() {
        guard let window = (connectedScenes.first as? UIWindowScene)?.windows.first else { return }
        let tapGesture = AnyGestureRecognizer(target: window, action: #selector(UIView.endEditing))
        tapGesture.requiresExclusiveTouchType = false
        tapGesture.cancelsTouchesInView = false
        tapGesture.delegate = self
        window.addGestureRecognizer(tapGesture)
    }
}

extension UIApplication: UIGestureRecognizerDelegate {
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true // set to `false` if you don't want to detect tap during other gestures
    }
}

class AnyGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if let touchedView = touches.first?.view, touchedView is UIControl {
            state = .cancelled

        } else if let touchedView = touches.first?.view as? UITextView, touchedView.isEditable {
            state = .cancelled

        } else {
            state = .began
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        state = .ended
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
        state = .cancelled
    }
}

Then call UIApplication.shared.addTapGestureRecognizer() inside of an .onAppear() block on your main root view.

I suggest you follow the links provided and read their answers and the comments to better understand the how and why.

James Futures
  • 185
  • 2
  • 12