在 SwiftUI 中检测并转发屏幕上任意位置的点击

Arm*_*man 5 ios swift swiftui

在 SwiftUI 应用程序中,我需要检测屏幕上的任何点击。只检测,然后将其转发到底层视图。作为一个用例,考虑将“在线”用户状态更新发送到服务器以响应任何用户活动。

我当然不想为此目的向每个视图添加手势识别器。我尝试在 SceneDelegate 中添加一个全局的。只要检测到任何点击,这都有效:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

但这会破坏一些 SwiftUI 控件。例如,按钮可以工作,但 TabView 不再响应点击。

我尝试了另一种方式,simultaneousGesture按照这里的建议使用:

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") }))
    }
}
Run Code Online (Sandbox Code Playgroud)

相同的结果,按钮工作,但 TabView 坏了。

任何想法如何让这个工作?

paw*_*222 13

作为一个用例,请考虑将“在线”用户状态更新发送到服务器以响应任何用户活动。

  1. 为全局手势识别器创建扩展:
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)
    }
}
Run Code Online (Sandbox Code Playgroud)
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
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 将其添加到根窗口:

SwiftUI 1 版本:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {        
    // ...
    addGestureRecognizer()
}
Run Code Online (Sandbox Code Playgroud)

SwiftUI 2 版本:

@main
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear(perform: UIApplication.shared.addGestureRecognizer)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 现在手势识别器已附加到根窗口,并将与其他控件同时识别:
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")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以AnyGestureRecongizer按照此处的建议创建自定义来检测任何手势。


Chr*_*ris 0

你尝试过设置吗

 cancelsTouchesInView = false 
Run Code Online (Sandbox Code Playgroud)

在你的 TapGesture 中?