简体   繁体   English

如何检测 swiftUI 中的摇晃手势

[英]How to detect shake gesture in swiftUI

In apple's documentation, I did not find any gesture related to shake for swiftUI.在苹果的文档中,我没有找到任何与 swiftUI 的抖动相关的手势。 So how to detect it?那么如何检测呢?

I am very new to swift programming and this question really bothers me for a long time.我对 swift 编程非常陌生,这个问题确实困扰了我很长时间。

In UIKit If I want to detect shake gesture is quite simple and straight forward.在 UIKit 中,如果我想检测摇晃手势非常简单直接。 In swiftUI, there are bunch of gestures like tap drag rotation, however I cannot find shake gesture in official documentation or anyone who asked.在 swiftUI 中,有很多手势,例如点击拖动旋转,但是我在官方文档或任何询问的人中都找不到摇动手势。 Is this possible in swiftUI to achieve the same result?这是否可能在 swiftUI 中达到相同的结果? Or they just forget to add it into the swiftUI framework...或者他们只是忘记将其添加到 swiftUI 框架中......

If it's not possible in swiftUI, then how am I going to import the motionEnded function in UIKit to the swiftUI view that I want to detect shake motion? If it's not possible in swiftUI, then how am I going to import the motionEnded function in UIKit to the swiftUI view that I want to detect shake motion?

You can add a new notification and override UIWindow.motionEnded in an extension (as also mentioned in an earlier answer):您可以在扩展中添加新通知并覆盖UIWindow.motionEnded (如之前的答案中所述):

extension NSNotification.Name {
    public static let deviceDidShakeNotification = NSNotification.Name("MyDeviceDidShakeNotification")
}

extension UIWindow {
    open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        super.motionEnded(motion, with: event)
        NotificationCenter.default.post(name: .deviceDidShakeNotification, object: event)
    }
}

With this in place, it's easy to subscribe to the notification in your view:有了这个,很容易在你的视图中订阅通知:

struct Example: View {
    @State private var message = "Unshaken"

    var body: some View {
        Text(message)
            .onReceive(NotificationCenter.default.publisher(for: .deviceDidShakeNotification)) { _ in
                self.message = "Shaken, not stirred."
        }
    }
}

You can do this from the ViewController level.您可以从 ViewController 级别执行此操作。 For example:例如:

final class MyVC: UIHostingController<ContentView> {

    override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        guard motion == .motionShake else { return }

        // Notify view.
    }
}

I am trying to solve the same problem.我正在尝试解决同样的问题。 Any success for you.对你来说任何成功。 I am able to detect shakes within AppDelegate, but have no idea how to access my SceneDelegate with my environmentObject我能够在 AppDelegate 中检测到抖动,但不知道如何使用我的 environmentObject 访问我的 SceneDelegate

I made this work using a view controller inside of a UIViewControllerRepresentable view.我使用UIViewControllerRepresentable视图中的视图 controller 完成了这项工作。

Corresponding UIViewControllerRepresentable and UIViewController :对应的UIViewControllerRepresentableUIViewController

struct ShakableViewRepresentable: UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> ShakableViewController {
        ShakableViewController()
    }
    func updateUIViewController(_ uiViewController: ShakableViewController, context: Context) {}
}


class ShakableViewController: UIViewController {

    override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        guard motion == .motionShake else { return }

        /* Do something */
        print("Shaken")
    }
}

How to implement it:如何实现它:

struct ContentView: View {

    var body: some View {
        ZStack {
            ShakableViewRepresentable()
                .allowsHitTesting(false)

            /* Other views, in VStack or whatever it may be */
        }
    }
}

iOS 13, Swift 5... a solution that works with SwiftUI based in jk2K answer, with this obviously this is in the ContentView.swift file. iOS 13, Swift 5... a solution that works with SwiftUI based in jk2K answer, with this obviously this is in the ContentView.swift file.

let messagePublisher = PassthroughSubject<String, Never>()

class ContentMode {

...

}

extension UIWindow {
 open override func motionEnded(_ motion: UIEvent.EventSubtype, with event:   UIEvent?) {
    if motion == .motionShake {
        print("Device shaken")
        messagePublisher.send("Stop Shaking Me")
    }
  }
}

You can than use this to pick it up in your UI您可以使用它在您的 UI 中选择它

.onReceive(messagePublisher) { (message) in
  // message = "Stop Shaking Me"
}

Here's a refinement on the above with a view modifier.这是使用视图修饰符对上述内容进行的改进。

import SwiftUI
import Combine

private let motionShaked = PassthroughSubject<Void, Never>()

extension UIWindow {
     open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        if motion == .motionShake {
            motionShaked.send()
        }
     }
}

private struct ShakeViewModifier: ViewModifier {
    let action: (() -> Void)

    init(perform action: @escaping (() -> Void)) {
        self.action = action
    }
        
    func body(content: Content) -> some View {
        content
            .onAppear {
            }
            .onReceive(motionShaked) {
                action()
            }
    }
}

extension View {
    public func onShake(perform action: @escaping (() -> Void)) -> some View {
        return self.modifier(ShakeViewModifier(perform: action))
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM