Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect shake gesture in swiftUI

Tags:

uikit

swiftui

In apple's documentation, I did not find any gesture related to shake for swiftUI. So how to detect it?

I am very new to swift programming and this question really bothers me for a long time.

In UIKit If I want to detect shake gesture is quite simple and straight forward. In swiftUI, there are bunch of gestures like tap drag rotation, however I cannot find shake gesture in official documentation or anyone who asked. Is this possible in swiftUI to achieve the same result? Or they just forget to add it into the swiftUI framework...

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?

like image 401
Lee Ronnie Avatar asked Oct 24 '19 21:10

Lee Ronnie


2 Answers

I made this work using a view controller inside of a UIViewControllerRepresentable view.

Corresponding UIViewControllerRepresentable and UIViewController:

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 */
        }
    }
}
like image 125
George Avatar answered Sep 28 '22 02:09

George


You can add a new notification and override UIWindow.motionEnded in an extension (as also mentioned in an earlier answer):

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."
        }
    }
}
like image 40
markiv Avatar answered Sep 28 '22 00:09

markiv