CodyMace
CodyMace

Reputation: 655

How to get motion events with the Apple TV remote

Has anybody figured out how to get motion events working with the new apple TV remote? Thanks.

I've tried calling

override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
    super.motionBegan(motion, withEvent: event)
    print("motion!")
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
    super.motionEnded(motion, withEvent: event)
    print("motion ended!")
}

With and without calling super gives me nothing.

Upvotes: 7

Views: 4410

Answers (3)

D. Pratt
D. Pratt

Reputation: 464

Thought I would update CodyMace's great answer with Swift 4.0 syntax

In appDelegate (You will need to import GameController here, too):

var motionDelegate: ReactToMotionEvents? = nil


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let center = NotificationCenter.default
    center.addObserver(self, selector: #selector(setupControllers), name: NSNotification.Name.GCControllerDidConnect, object: nil)
    center.addObserver(self, selector: #selector(setupControllers), name: NSNotification.Name.GCControllerDidDisconnect, object: nil)
    GCController.startWirelessControllerDiscovery { () -> Void in

    }
    return true
}

@objc func setupControllers(notif: NSNotification) {
    print("controller connection")
    let controllers = GCController.controllers()
    for controller in controllers {
        controller.motion?.valueChangedHandler = { (motion: GCMotion)->() in
            if let delegate = self.motionDelegate {
                delegate.motionUpdate(motion: motion)
            }
        }
    }
}

The protocol stays the same

protocol ReactToMotionEvents {
func motionUpdate(motion: GCMotion) -> Void

}

And where you want implemented

import SpriteKit  
import GameController  
class GameScene: SKScene, ReactToMotionEvents {  

    override func didMoveToView(view: SKView) {   
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.motionDelegate = self

    }  

    func motionUpdate(motion: GCMotion) {  
        print("x: \(motion.userAcceleration.x)   y: \(motion.userAcceleration.y)")  
    }  
}  

Upvotes: 2

CodyMace
CodyMace

Reputation: 655

A great swift example can be found here: https://forums.developer.apple.com/message/65560#65560 It's basically what Daniel Storm said above, but following this got it working for me. Here's what I did.

In appDelegate:

 var motionDelegate: ReactToMotionEvents? = nil   

     func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {  
        let center = NSNotificationCenter.defaultCenter()  
        center.addObserver(self, selector: "setupControllers:", name: GCControllerDidConnectNotification, object: nil)  
        center.addObserver(self, selector: "setupControllers:", name: GCControllerDidDisconnectNotification, object: nil)  
        GCController.startWirelessControllerDiscoveryWithCompletionHandler { () -> Void in  

        }  
        return true  
    }  

    func setupControllers(notif: NSNotification) {  
        print("controller connection")  
        let controllers = GCController.controllers()  
        for controller in controllers {  
            controller.motion?.valueChangedHandler = { (motion: GCMotion)->() in  
                if let delegate = self.motionDelegate {  
                    delegate.motionUpdate(motion)  
                }  
            }  
        }  
    }  

protocol ReactToMotionEvents {  
    func motionUpdate(motion: GCMotion) -> Void  
}  

Where I want it implemented, in my case an SKScene:

import SpriteKit  
import GameController  
class GameScene: SKScene, ReactToMotionEvents {  

    override func didMoveToView(view: SKView) {   
        let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate  
        appDelegate.motionDelegate = self  

    }  

    func motionUpdate(motion: GCMotion) {  
        print("x: \(motion.userAcceleration.x)   y: \(motion.userAcceleration.y)")  
    }  
}  

Upvotes: 6

Daniel Storm
Daniel Storm

Reputation: 18898

Via How to access motion & orientation information of remote:


First of all, one needs to use NSNotificationCenter to find the controllers. Probably best to do this when app launches. Something like this:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controllerDidConnect:) name:GCControllerDidConnectNotification object:nil];  

We can then use the following code after connecting to store the device info in a property:

- (void)controllerDidConnect:(NSNotification *)notification {  
    self.myController = notification.object;  
}  

The remote profile is a subclass of the micro gamepad profile. Motion and other data can be tracked by adding a value changed event handler:

  GCMicroGamepad *profile =  self.myController.microGamepad  
  profile.valueChangedHandler= ^ (GCMicroGamepad *gamepad, GCControllerElement *element) {  
        if (self.myController.motion) {  
            NSLog(@"motion supported");  
            NSLog(@"gravity: %f %f %f", self.myController.motion.gravity.x, self.myController.motion.gravity.y, self.myController.motion.gravity.z);  
            NSLog(@"userAcc: %f %f %f", self.myController.motion.userAcceleration.x, self.myController.motion.userAcceleration.y, self.myController.motion.userAcceleration.z);  
            NSLog(@"rotationRate: %f %f %f", self.myController.motion.rotationRate.x, self.myController.motion.rotationRate.y, self.myController.motion.rotationRate.z);  
            NSLog(@"attitude: %f %f %f %f", self.myController.motion.attitude.x, self.myController.motion.attitude.y, self.myController.motion.attitude.z, self.myController.motion.attitude.w);  
        }  
    };  

Upvotes: 2

Related Questions