TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

How to detect shake gestures

Paul Hudson    @twostraws   

Updated for Xcode 15

SwiftUI doesn’t have a built-in way to detect the user shaking their device, but it doesn’t take much work to create one yourself by overriding motionEnded() in UIWindow and creating a custom view modifier.

This takes five steps:

  1. Adding an extension to UIDevice to track a new notification that will be sent when a shake gesture happens.
  2. Create an extension on UIWindow to override the default motionEnded() method. This is where UIKit sends shake gestures, so you should look for that happening and translate it into your new notification.
  3. Create a custom view modifier to watch for the shake notification being posted, and call a function of your choosing when it happens.
  4. Create a View extension that wraps up your new modifier neatly.
  5. Try it out in a view.

Important: At the time of writing view modifiers do not work with onReceive() unless you first add onAppear(), which is why it appears above. Yes, it’s empty, but it acts as a workaround for the problem.

Here’s a complete code sample walking through all five steps with comments:

// The notification we'll send when a shake gesture happens.
extension UIDevice {
    static let deviceDidShakeNotification = Notification.Name(rawValue: "deviceDidShakeNotification")
}

//  Override the default behavior of shake gestures to send our notification instead.
extension UIWindow {
     open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
        if motion == .motionShake {
            NotificationCenter.default.post(name: UIDevice.deviceDidShakeNotification, object: nil)
        }
     }
}

// A view modifier that detects shaking and calls a function of our choosing.
struct DeviceShakeViewModifier: ViewModifier {
    let action: () -> Void

    func body(content: Content) -> some View {
        content
            .onAppear()
            .onReceive(NotificationCenter.default.publisher(for: UIDevice.deviceDidShakeNotification)) { _ in
                action()
            }
    }
}

// A View extension to make the modifier easier to use.
extension View {
    func onShake(perform action: @escaping () -> Void) -> some View {
        self.modifier(DeviceShakeViewModifier(action: action))
    }
}

// An example view that responds to being shaken
struct ContentView: View {
    var body: some View {
        Text("Shake me!")
            .onShake {
                print("Device shaken!")
            }
    }
}

As you can see, once you have the first four steps in place you can go ahead and add an onShake() modifier to any view you want, providing some custom code to run when the shake gesture happens – it’s not straightforward to set up, but once you’re done it all works neatly.

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Similar solutions…

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.