SwiftUI and Shake Gesture

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.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

Leave a comment

Your email address will not be published. Required fields are marked *

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts