tvOS - catch MENU button presses during push-view-controller transition

1.6k Views Asked by At

I have a UIViewController in my tvOS app which will only appear for a few seconds, and needs totally customizable MENU button handling. I create it like so:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Add a tap gesture recognizer to handle MENU presses.
    UITapGestureRecognizer *tapGestureRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
    tapGestureRec.allowedPressTypes = @[@(UIPressTypeMenu)];
    [self.view addGestureRecognizer:tapGestureRec];
}

- (void)handleTap:(UITapGestureRecognizer *)sender
{
    // Code to process the MENU button is here.
}

I display the view controller using pushViewController:animated::

UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
[self pushViewController:controller animated:isAnimated];

I've found that if the user presses MENU as soon as the screen starts to appear, while the cross-fade transition effect is still displaying, they are able to dodge the UITapGestureRecognizer and go back to a previous screen, which is not intended. They can also cause issues by mashing MENU over and over again--eventually they will escape out of things that they should not be able to.

How can I ensure that the MENU press always reaches my override? Is there a way to specify an app-encompassing MENU button handler and still use a UINavigationController?

2

There are 2 best solutions below

2
On BEST ANSWER

A solution that worked for me was to install a UITapGestureRecognizer onto self.navigationController.view. Any taps which get missed by the regular UIViewControllers end up getting caught by the navigation controller's recognizer instead. If you install UITapGestureRecognizers on each view controller you create, the only taps which will fall through the cracks to the navigation controller are taps that occur mid-transition, and it is safe to just ignore those taps completely.

Note that when you want MENU to go back to the home screen, you will need to temporarily remove this tap recognizer and let tvOS handle the tap on its own, as I could find no clean way to escape to the home screen in my code (short of exit(0)).

0
On

I have very similar problem because of this behaviour. In my case I have custom focus management depending on MENU button click detected on pressesEnded and managed by preferredFocusedView. But when navigation controller is poping ViewController and user at this time make second click on MENU button, then UINavigationController class detect pressesEnded and then call preferredFocusedView on my destination ViewController where is my management but pressesEnded will be not called, because was "stolen" by UINavigationController.

Solution for this problem in my case is to create "dummy" class that will be used by UINavigationController like this:

class MenuPhysicalHackNavigationController: UINavigationController {

    override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    }

    override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    }

    override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    }

    override func pressesChanged(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    }

}