< How to detect the user hovering over a view | How to control the tappable area of a view using contentShape() > |
Updated for Xcode 14.2
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:
UIDevice
to track a new notification that will be sent when a shake gesture happens.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.View
extension that wraps up your new modifier neatly.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.
SPONSORED From March 20th to 26th, you can join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.