Skip to content

Commit 75284d3

Browse files
douglowderfacebook-github-bot
authored andcommitted
Apple TV: Enable long presses on TV remote; dev menu on TV device; example code
Summary: **Motivation** Properly support long presses on the Apple TV remote, and also enable dev menu functionality on a real Apple TV device (shaking an Apple TV doesn't work 😄 ) **Test plan** New example added to `RNTester`. Closes facebook#15221 Differential Revision: D5526463 Pulled By: javache fbshipit-source-id: a61051e86bc82a9561eefc1704bed6b1f2617e05
1 parent e61257c commit 75284d3

File tree

6 files changed

+142
-4
lines changed

6 files changed

+142
-4
lines changed

RNTester/js/RNTesterList.ios.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,11 @@ const APIExamples: Array<RNTesterExample> = [
336336
module: require('./TransformExample'),
337337
supportsTVOS: true,
338338
},
339+
{
340+
key: 'TVEventHandlerExample',
341+
module: require('./TVEventHandlerExample'),
342+
supportsTVOS: true,
343+
},
339344
{
340345
key: 'VibrationExample',
341346
module: require('./VibrationExample'),
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Copyright (c) 2017-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @flow
10+
* @providesModule TVEventHandlerExample
11+
*/
12+
'use strict';
13+
14+
var React = require('react');
15+
var ReactNative = require('react-native');
16+
17+
var {
18+
Platform,
19+
StyleSheet,
20+
View,
21+
Text,
22+
TouchableOpacity,
23+
TVEventHandler,
24+
} = ReactNative;
25+
26+
exports.framework = 'React';
27+
exports.title = 'TVEventHandler example';
28+
exports.description = 'iOS alerts and action sheets';
29+
exports.examples = [{
30+
title: 'TVEventHandler',
31+
render() {return <TVEventHandlerView/>;}
32+
}];
33+
34+
class TVEventHandlerView extends React.Component {
35+
state: {
36+
lastEventType: string
37+
}
38+
39+
constructor(props) {
40+
super(props);
41+
this.state = {
42+
lastEventType: ''
43+
};
44+
}
45+
46+
_tvEventHandler: any;
47+
48+
_enableTVEventHandler() {
49+
this._tvEventHandler = new TVEventHandler();
50+
this._tvEventHandler.enable(this, function(cmp, evt) {
51+
cmp.setState({
52+
lastEventType: evt.eventType
53+
})
54+
});
55+
}
56+
57+
_disableTVEventHandler() {
58+
if (this._tvEventHandler) {
59+
this._tvEventHandler.disable();
60+
delete this._tvEventHandler;
61+
}
62+
}
63+
64+
componentDidMount() {
65+
this._enableTVEventHandler();
66+
}
67+
68+
componentWillUnmount() {
69+
this._disableTVEventHandler();
70+
}
71+
72+
render() {
73+
74+
if (Platform.isTVOS) {
75+
return (
76+
<View>
77+
<TouchableOpacity onPress={() => {}}>
78+
<Text>
79+
This example enables an instance of TVEventHandler to show the last event detected from the Apple TV Siri remote or from a keyboard.
80+
</Text>
81+
</TouchableOpacity>
82+
<Text style={{color: 'blue'}}>
83+
{this.state.lastEventType}
84+
</Text>
85+
</View>
86+
);
87+
} else {
88+
return (
89+
<View>
90+
<Text>
91+
This example is intended to be run on Apple TV.
92+
</Text>
93+
</View>
94+
);
95+
}
96+
}
97+
}

React/Base/RCTTVRemoteHandler.m

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#import "RCTView.h"
2323
#import "UIView+React.h"
2424

25+
#import "RCTDevMenu.h"
26+
2527
@implementation RCTTVRemoteHandler {
2628
NSMutableArray<UIGestureRecognizer *> *_tvRemoteGestureRecognizers;
2729
}
@@ -60,7 +62,15 @@ - (instancetype)init
6062
// Right
6163
[self addTapGestureRecognizerWithSelector:@selector(swipedRight:)
6264
pressType:UIPressTypeRightArrow];
63-
65+
66+
// Recognizers for long button presses
67+
// We don't intercept long menu press -- that's used by the system to go to the home screen
68+
69+
[self addLongPressGestureRecognizerWithSelector:@selector(longPlayPausePressed:)
70+
pressType:UIPressTypePlayPause];
71+
72+
[self addLongPressGestureRecognizerWithSelector:@selector(longSelectPressed:)
73+
pressType:UIPressTypeSelect];
6474

6575
// Recognizers for Apple TV remote trackpad swipes
6676

@@ -100,9 +110,19 @@ - (void)selectPressed:(UIGestureRecognizer *)r
100110
[self sendAppleTVEvent:@"select" toView:r.view];
101111
}
102112

103-
- (void)longPress:(UIGestureRecognizer *)r
113+
- (void)longPlayPausePressed:(UIGestureRecognizer *)r
114+
{
115+
[self sendAppleTVEvent:@"longPlayPause" toView:r.view];
116+
117+
// If shake to show is enabled on device, use long play/pause event to show dev menu
118+
#if RCT_DEV
119+
[[NSNotificationCenter defaultCenter] postNotificationName:RCTShowDevMenuNotification object:nil];
120+
#endif
121+
}
122+
123+
- (void)longSelectPressed:(UIGestureRecognizer *)r
104124
{
105-
[self sendAppleTVEvent:@"longPress" toView:r.view];
125+
[self sendAppleTVEvent:@"longSelect" toView:r.view];
106126
}
107127

108128
- (void)swipedUp:(UIGestureRecognizer *)r
@@ -127,6 +147,14 @@ - (void)swipedRight:(UIGestureRecognizer *)r
127147

128148
#pragma mark -
129149

150+
- (void)addLongPressGestureRecognizerWithSelector:(nonnull SEL)selector pressType:(UIPressType)pressType
151+
{
152+
UILongPressGestureRecognizer *recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:selector];
153+
recognizer.allowedPressTypes = @[@(pressType)];
154+
155+
[_tvRemoteGestureRecognizers addObject:recognizer];
156+
}
157+
130158
- (void)addTapGestureRecognizerWithSelector:(nonnull SEL)selector pressType:(UIPressType)pressType
131159
{
132160
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:selector];

React/DevSupport/RCTDevMenu.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
#import <React/RCTBridge.h>
1313
#import <React/RCTBridgeModule.h>
1414

15+
#if RCT_DEV
16+
17+
RCT_EXTERN NSString *const RCTShowDevMenuNotification;
18+
19+
#endif
20+
21+
1522
@class RCTDevMenuItem;
1623

1724
/**

React/DevSupport/RCTDevMenu.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
#if RCT_DEV
1818

19-
static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification";
19+
NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification";
2020

2121
@implementation UIWindow (RCTDevMenu)
2222

docs/BuildingForAppleTV.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class Game2048 extends React.Component {
8383
}
8484

8585
```
86+
- *Dev Menu support*: On the simulator, cmd-D will bring up the developer menu, just like on iOS. To bring it up on a real Apple TV device, make a long press on the play/pause button on the remote. (Please do not shake the Apple TV device, that will not work :) )
8687
8788
- *TV remote animations*: `RCTTVView` native code implements Apple-recommended parallax animations to help guide the eye as the user navigates through views. The animations can be disabled or adjusted with new optional view properties.
8889

0 commit comments

Comments
 (0)