0

Capture Circular Gestures from Siri Remote 2nd Generation

 3 years ago
source link: https://dcordero.me/posts/capture_circular_gestures_on_siri_remote_2nd_generation.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

David Cordero

Capture Circular Gestures from Siri Remote 2nd Generation

Published on 01 Sep 2021

Recently, together with the release of the new Apple TV 4K, Apple released a new generation of their beloved and at the same time hated Siri Remote controller.

One of the features that Apple announced for this remote control, was the possibility of scrubbing content in AVPlayerViewController using a circular gesture similar to the classic click wheel of the first iPods.

Sadly for developers, they did not present any new API to make it easier for us to adopt this new gesture in our Apps. In addition to that, I was told to use AVPlayerViewController as the only way to get this gesture when I explicitly asked about how to implement it in the Apple developer forums.

This post is the result of my attempts to capture this new gesture.

Getting the info from the digitizer

As we already saw in “Directional clicks on tvOS”, there is no way to get the precise location of the finger in the digitizer of Siri Remote 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.

Nevertheless, thanks to the 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)

In the following code snippet, you can find an example of how we can capture and log the exact location of the finger in the digitizer.

import UIKit
import GameController

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        setUpControllerObserver()
    }
    
    // MARK: - Private

    private func setUpControllerObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(controllerConnected(note:)), name: .GCControllerDidConnect, object: nil)
    }
    
    @objc
    private func controllerConnected(note: NSNotification) {
        
        // Only considering a single controller for simplicity
        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
            print("[\(x), \(y)])
        }
    }
}

From these logs we can find out that our space of coordinates looks like the following image:

Discarding gestures

Once that we know better our working environment, the next step is detecting when the user is touching the outer ring or the internal part of the digitizer.

We can do that by calculating the radius from the user’s finger to the center of the digitizer, and discarding the gestures that are too close to the center.

Using the following image as reference, what we want is to filter gestures with a radius in the green area.

This is something we can do with the following code. After testing with multiple values, I came out with a threshold of 0.5, which seems to be working pretty well.

// Get the distance from the center of the digitizer to the gesture location
let radius = sqrt(x*x + y*y)

// Discard gestures out of the ring area of Siri Remote
guard radius > 0.5 else {
    return
}

Making the gesture more visual

The next step is to give some visual feedback to the user for them to understand that the gesture is working. For this playground project, I opted for adding a hint view with a transparent background in top of an image of Siri Remote.

If you want to use this solution in your project, you will need to adjust this part to your visual requirements.

With some simple trigonometry, we can easily calculate the angle that we should rotate the hint view.

The following code snippet is the code doing the actual maths and the rotation.

// Rotate hintView to the appropriate radians
let cos = x / radius
let sin = y / radius
let radians = atan2(sin, cos)

hintView.transform = CGAffineTransform(rotationAngle: CGFloat(-radians))

To make it easier to understand what is happening behind the scene, here you can see another version using a colored background.

Getting the Gesture Direction (⬅️ vs ➡️)

So far we alredy have a pretty good-looking result. But in practice we will also need the direction of the gesture, that is at the end the information that will allow us to change the value in a slider.

The following code shows the maths to get and to log the direction of the circular gesture.

let normalizedRadians = (radians + (2 * .pi)).truncatingRemainder(dividingBy: 2 * .pi)       
let radiansOffset = normalizedRadians - self.currentRadians
let normalizedRadiansOffset = (radiansOffset + (2 * .pi)).truncatingRemainder(dividingBy: 2 * .pi)

print(normalizedRadiansOffset > .pi ? "➡️" : "⬅️")

Show me the code

For better understanding, please find here a project showing a working implementation of the method described in this post.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK