David Cordero
Directional clicks on tvOS
Published on 10 Jul 2017
If you have tried to detect directional clicks on tvOS you might have notice that it is not that easy.
Checking the documentation of UITapRecognizers and UIPressType, you will find the following list of available press types:
@available(tvOS 9.0, *)
public enum UIPressType : Int {
case upArrow
case downArrow
case leftArrow
case rightArrow
case select
case menu
case playPause
}
But as soon as you try to use them, you will notice that the types upArrow, downArrow, leftArrow or rightArrow are only triggered as result of a tap gesture. When you click on the touchpad of Siri Remote, the only gesture that is triggered is select
, regardless of the position of your finger during the click.
In addition to that, in tvOS, there is no way to get the precise location of the finger in the digitizer from an instance of UITouch. In fact, in order to avoid people creating pointer-based applications, the coordinates of any gesture in Siri Remote always start from the center of the touchpad wherever you actually start the gesture from.
GameController SDK to the W̶O̶R̶K̶A̶R̶O̶U̶N̶D̶ RESCUE !!
Thanks to GameController SDK we can have a lower level of abstraction with the controllers engine. And, lucky for us… it does allow getting the absolute directional pad values from the controllers (in our case, from Siri Remote)
So, we could get the directional pad information…
import UIKit
import GameController
enum DPadState {
case select
case right
case left
case up
case down
}
class ViewController: UIViewController {
private var dPadState: DPadState = .select
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setUpDirectionalPad()
}
// MARK: - Private
private func setUpDirectionalPad() {
guard let controller = GCController.controllers().first else { return }
guard let micro = controller.microGamepad else { return }
micro.reportsAbsoluteDpadValues = true
micro.dpad.valueChangedHandler = {
[weak self] (pad, x, y) in
let threshold: Float = 0.7
if y > threshold {
self?.dPadState = .up
}
else if y < -threshold {
self?.dPadState = .down
}
else {
self?.dPadState = .select
}
}
}
}
… and depending on that, we can process the different actions whenever the gesture with type .select is triggered:
func pressesBegan(_ presses: Set<uipress>, with event: UIPressesEvent?) {
for press in presses {
switch press.type {
case .select where dPadState == .up:
print("⬆️")
case .select where dPadState == .down:
print("⬇️")
case .select:
print("🆗")
default:
super.pressesBegan(presses, with: event)
}
}
}
Show me the code:
I have created this simple project that detects directional clicks on Siri Remote with the method previously described.
Feel free to follow me on github, twitter or dcordero.me if you have any further question.