There isn’t a way to catch the shake gesture only using SwiftUI, so it’s necessary use these frameworks: Uikit and Combine.
Combine is very helpful in this case because how is wrote in the Apple documentation: “Combine declares publishers to expose values that can change over time, and subscribers to receive those values from the publishers.”
In the example, the goal is catch the Shake gesture with the UiKit, with the Combine framework we publish this event and get the combine published info in the SwiftUI view that is a subscriber. Let’s start.
We must create a Swift file, ShakeGestureManager.swift
import Foundation
import SwiftUI
import Combine
let messagePublisher = PassthroughSubject<Void, Never>()
class ShakeViewController: UIViewController {
override func motionBegan(_ motion: UIEvent.EventSubtype,
with event: UIEvent?) {
guard motion == .motionShake else { return }
messagePublisher.send()
}
}
struct ShakeViewRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) ->
ShakeViewController {
ShakeViewController()
}
func updateUIViewController(_ uiViewController: ShakeViewController,
context: Context) {
}
}
Where the PassthroughSubject is a “A subject that broadcasts elements to downstream subscribers.” (from the Apple documentation). We need it to communicate with the SwiftUI View.
The ShakeViewController is a simple UIViewController that catches the shake Gesture. Like in the case of the Map, it’s not possible to use the UIViewController in the SwiftUI so we must create a struct that implement the UIViewControllerRepresentable that we can use in the SwiftUI View.
Now see the SwiftUI view:
import SwiftUI
struct ContentView: View {
@State var shaked = false
var body: some View {
NavigationView {
VStack{
ZStack {
ShakeViewRepresentable()
.allowsHitTesting(false)
VStack {
Text("Shake device to change view!")
}
}.onReceive(messagePublisher) { _ in
self.shaked = true
}
NavigationLink(destination: SecondView(), isActive: $shaked) {
EmptyView()
}
}
}
}
}
The state variable shaked it used to store the shaked event. In the navigationview there is a ZStack, where at the bottom (first element) there is the ShakeViewRepresentable that can’t get touch event from the user because allowsHitTesting is false, on the top there is a simple Text message.
When the ZStack receives the messagePublisher, the shaked variable become true and the NavigationLink become active so the view navigates to the SecondView (that you can create how you want). Note the use of the EmptyView from the NavigationLink, it used to show nothing but to have a working link.
That’s all. I hope that it can be helpful. You can get the code here.