Search code examples
iosswiftuievent

How can I handle support for shaking the ios device from my swift app?


I'm writing an ios swift app and I want to include a support for shaking the device. As for now I want to print a msg to the console when user shakes his phone. I found this tutorial http://www.ioscreator.com/tutorials/detect-shake-gesture-ios8-swift and it looks super easy, however there's one thing that bothers me.

I want it to work from any view in the app, not only a single one. So no matter where user currently is in the app - he should be able to invoke the shake method. Should I implement the method override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) { in every panel then? Or is there a way of implementing it once and populating it across the whole app?


Solution

  • First of all lets find out where these 'motion' methods come from, as docs say:

    The UIResponder class defines an interface for objects that respond to and handle events. It is the superclass of UIApplication, UIView and its subclasses.. (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/)

    The event-handling methods for motion events are:

    func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?)
    func motionCancelled(motion: UIEventSubtype, withEvent event: UIEvent?)
    func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?)
    

    So, as you see, to 'catch' motion events on every screen of the app - we should override these methods in these screens. Thanks God, with extensions - we can make it much easier :)

    To incapculate 'motion' logic lets make a protocol and name it 'MotionDelegate':

    protocol MotionDelegate {
    func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?)
    func motionCancelled(motion: UIEventSubtype, withEvent event: UIEvent?)
    func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?)
    

    }

    And make an extension of UIViewController, conforming MotionDelegate protocol:

    extension UIViewController:MotionDelegate {
    override public func becomeFirstResponder() -> Bool {
        return true
    }
    
    override public func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
        if motion == .MotionShake { print("Shaking motionBegan with event\(event)") }
    }
    
    override public func motionCancelled(motion: UIEventSubtype, withEvent event: UIEvent?) {
        if motion == .MotionShake { print("Shaking motionCancelled with event\(event)") }
    }
    
    override public func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
        if motion == .MotionShake { print("Shaking motionEnded with event\(event)") }
    }
    

    }

    In this way motion handling will be working on EVERY UIViewController instances of your appliction.

    To handle motion event on some certain vc your should override it in its extension:

    extension MotionViewController {
    override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
        if motion == .MotionShake {
            print("MotionViewController Shaking motionEnded with event\(event)")
        }
    }
    

    }

    Hope it helps!