There may come a time when you’d like to shake a macOS application’s window (or one of them) to reinforce an event. In my case, I am creating a macOS application that serves as a conduit to control aspects of a user experience. It communicates with an iOS application which serves as a kind of remote control for the macOS application. And settings are synchronized back and forth. It’s pretty cherry.
macOS Sierra and iOS 10 using Swift 3 are quite similar. I know enough about iOS development to make myself a drunken lumberjack in a glade of birch trees – swinging wildly hoping to hit things. Anyway, I am using MultipeerConnectivity framework for the communication discovery and data transfer (at a top-level explanation). When a connection is made (the iOS app advertises), I turn a little grey dot into a green one to show this state. When a disconnection is made, I turn it back to grey.
However, this might not be enough. I could play a disconnection tone (and I still might), but I wanted to shake the window to let someone know, “Hey! I lost a connection.” This sets expectations and it’s a cool effect I wanted to try out.
I have an extension that will do this, written for Swift 3. It’s implementation is a little different than for previous versions of Swift. Here is the extension. The Cocoa import is because I keep separate extension swift files instead of in-lining everything into a view controller, etc. This keeps things cleaner and a little easier for me and others to find.
import Cocoa /** window?.shakeWindow() view.window?.shakeWindow() NSApp.windows.first?.shakeWindow() */ extension NSWindow { func shakeWindow(){ let numberOfShakes = 3 let durationOfShake = 0.3 let vigourOfShake : CGFloat = 0.04 let frame : CGRect = self.frame let shakeAnimation :CAKeyframeAnimation = CAKeyframeAnimation() let shakePath = CGMutablePath() shakePath.move( to: CGPoint(x:NSMinX(frame), y:NSMinY(frame))) for _ in 0...numberOfShakes-1 { shakePath.addLine(to: CGPoint(x:NSMinX(frame) - frame.size.width * vigourOfShake, y:NSMinY(frame))) shakePath.addLine(to: CGPoint(x:NSMinX(frame) + frame.size.width * vigourOfShake, y:NSMinY(frame))) } shakePath.closeSubpath() shakeAnimation.path = shakePath shakeAnimation.duration = durationOfShake self.animations = ["frameOrigin":shakeAnimation] self.animator().setFrameOrigin(self.frame.origin) } }
I certainly hope that you enjoy it.