In a SwiftUI app, I need to detect any tap on the screen. Only detect, and then forward it to underlying views. As a use case, think of sending "online" user status updates to the server in response to any user activity.
I certainly don't want to add gesture recognizers to every view for this purpose. I tried adding a global one in SceneDelegate. This works as far as detecting any tap goes:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// ... snip other code
initGlobalTapRecognizer()
}
// MARK: UIGestureRecognizerDelegate
extension SceneDelegate: UIGestureRecognizerDelegate {
func initGlobalTapRecognizer() {
let tapGesture = UITapGestureRecognizer(target: self, action: nil)
tapGesture.delegate = self
window?.addGestureRecognizer(tapGesture)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
print("tapped")
return true
}
}
But this breaks some SwiftUI controls. For example, buttons work, but TabView no longer responds to taps.
I tried another way, using simultaneousGesture
as suggest here:
struct ContentView: View {
@State var selectedTab: Int = 1
var body: some View {
TabView(selection: $selectedTab) {
VStack {
Text("tab 1")
Button(action: { print("button 1 click") }, label: { Text("button 1") })
}
.tabItem( { Text("tab 1") } )
.tag(1)
VStack {
Text("tab 2")
}
.tabItem( { Text("tab 2") } )
.tag(2)
}
.contentShape(Rectangle())
.simultaneousGesture(TapGesture().onEnded({ print("simultaneous gesture tap") }))
}
}
Same result, buttons work, but TabView is broken.
Any ideas how to get this working?
Gestures performed within the bounds of a view can be detected by adding a gesture recognizer to that view. SwiftUI provides recognizers for tap, long press, rotation, magnification (pinch) and drag gestures. A gesture recognizer is added to a view using the gesture () modifier, passing through the gesture recognizer to be added.
If you want to try out the code, create a new project using the Single View Application template and make sure you select SwiftUI as the UI option. Then paste the code in ContentView.swift.
Each swipe gesture recognizer can only detect swipes in a single direction. That means if you want to detect an up and down swipe, you’ll need to define two separate swipe gesture recognizers where one recognizes only up swipes and the second only recognizes down swipes.
Move the mouse pointer over the Pan Gesture Recognizer icon in the Document Outline or at the top of the View Controller, hold down the Control key, and Ctrl-drag from the Pan Gesture Recognizer above the last curly bracket in the ViewController. swift file. Release the Control key and the left mouse button.
As a use case, think of sending "online" user status updates to the server in response to any user activity.
extension UIApplication {
func addGestureRecognizer() {
guard let window = windows.first else { return }
let gesture = UITapGestureRecognizer(target: window, action: nil)
gesture.requiresExclusiveTouchType = false
gesture.cancelsTouchesInView = false
gesture.delegate = self
window.addGestureRecognizer(gesture)
}
}
extension UIApplication: UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
print("touch detected")
return true
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
SwiftUI 1 version:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// ...
addGestureRecognizer()
}
SwiftUI 2 version:
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear(perform: UIApplication.shared.addGestureRecognizer)
}
}
}
struct ContentView: View {
var body: some View {
TabView {
Button("Tap me") {
print("Button tapped")
}
.tabItem {
Text("One")
}
List {
Text("Hello")
Text("World")
}
.tabItem {
Text("Two")
}
}
}
}
Alternatively, you can create a custom AnyGestureRecongizer
as proposed here to detect any gestures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With