01
Jul
2021
Shake Gesture in SwiftUI
Reading time: 1 min
This recipe shows how to implement shake gesture detection in SwiftUI.
The result looks like this (you'll have to image the phone being shaken :)).
This component is available as a Swift Package in this repo.
The recipe is as follows:
- Override
UIWindow
'smotionEnded
formotionShake
and have it post a notification. - Receive the notification in your view via
onReceive
and trigger your action.
To make things nicer, we'll wrap the reception part in a ViewModifier
and expose a nice View
extension to handle everything.
OK, first define the Notification.Name
, as you generally should if you're working with custom notification:
public extension Notification.Name {
static let shakeEnded = Notification.Name("ShakeEnded")
}
Then, detect the shake gesture in the UIWindow
extension:
public extension UIWindow {
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
if motion == .motionShake {
NotificationCenter.default.post(name: .shakeEnded, object: nil)
}
super.motionEnded(motion, with: event)
}
}
Next, add this ViewModifier
that adds a onReceive
that specifically captures shakeEnded
notification:
struct ShakeDetector: ViewModifier {
let onShake: () -> Void
func body(content: Content) -> some View {
content
.onAppear() // this has to be here because of a SwiftUI bug
.onReceive(NotificationCenter.default.publisher(for: .shakeEnded)) { _ in
onShake()
}
}
}
Finally, wrap everything in this nice extension:
extension View {
func onShake(perform action: @escaping () -> Void) -> some View {
self.modifier(ShakeDetector(onShake: action))
}
}
And that's it! Now, you can detect shake gesture in any view:
struct ShakeTest: View {
@State private var text = "Shake me!"
var body: some View {
Text(text)
.onShake { // ADD THIS
text = "Shaken at \(Date())"
}
}
}