Apple TV support 4: support for input (tvOS focus engine)
Reviewed By: shergin Differential Revision: D4333546 fbshipit-source-id: 8655070e81dbb62a80ab1f00a43ef6c2d9654618
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 39 KiB |
@ -30,6 +30,7 @@
|
|||||||
- (void)setUp
|
- (void)setUp
|
||||||
{
|
{
|
||||||
_runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp", nil);
|
_runner = RCTInitRunnerForApp(@"IntegrationTests/IntegrationTestsApp", nil);
|
||||||
|
_runner.recordMode = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Test harness
|
#pragma mark - Test harness
|
||||||
|
@ -41,12 +41,14 @@
|
|||||||
[_runner runTest:_cmd module:@#name]; \
|
[_runner runTest:_cmd module:@#name]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !TARGET_OS_TV // None of these will run in tvOS due to StatusBar not existing
|
|
||||||
RCT_TEST(ViewExample)
|
RCT_TEST(ViewExample)
|
||||||
RCT_TEST(LayoutExample)
|
RCT_TEST(LayoutExample)
|
||||||
RCT_TEST(TextExample)
|
RCT_TEST(TextExample)
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
// No switch or slider available on tvOS
|
||||||
RCT_TEST(SwitchExample)
|
RCT_TEST(SwitchExample)
|
||||||
RCT_TEST(SliderExample)
|
RCT_TEST(SliderExample)
|
||||||
|
// TabBarExample on tvOS passes locally but not on Travis
|
||||||
RCT_TEST(TabBarExample)
|
RCT_TEST(TabBarExample)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -213,7 +213,10 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a
|
|||||||
(void)rootView;
|
(void)rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV // userInteractionEnabled is true for Apple TV views
|
||||||
XCTAssertFalse(rootContentView.userInteractionEnabled, @"RCTContentView should have been invalidated");
|
XCTAssertFalse(rootContentView.userInteractionEnabled, @"RCTContentView should have been invalidated");
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testUnderlyingBridgeIsDeallocated
|
- (void)testUnderlyingBridgeIsDeallocated
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const Platform = require('Platform');
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
var ReactNative = require('react-native');
|
var ReactNative = require('react-native');
|
||||||
var {
|
var {
|
||||||
@ -190,10 +191,10 @@ exports.examples = [
|
|||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Text style={{fontFamily: 'Cochin'}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Times' : 'Cochin')}}>
|
||||||
Cochin
|
Cochin
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Cochin', fontWeight: 'bold'}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Times' : 'Cochin'), fontWeight: 'bold'}}>
|
||||||
Cochin bold
|
Cochin bold
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Helvetica'}}>
|
<Text style={{fontFamily: 'Helvetica'}}>
|
||||||
@ -202,10 +203,10 @@ exports.examples = [
|
|||||||
<Text style={{fontFamily: 'Helvetica', fontWeight: 'bold'}}>
|
<Text style={{fontFamily: 'Helvetica', fontWeight: 'bold'}}>
|
||||||
Helvetica bold
|
Helvetica bold
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Verdana'}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Courier' : 'Verdana')}}>
|
||||||
Verdana
|
Verdana
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Verdana', fontWeight: 'bold'}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Courier' : 'Verdana'), fontWeight: 'bold'}}>
|
||||||
Verdana bold
|
Verdana bold
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@ -565,10 +566,10 @@ exports.examples = [
|
|||||||
<Text style={{fontVariant: ['small-caps']}}>
|
<Text style={{fontVariant: ['small-caps']}}>
|
||||||
Small Caps{'\n'}
|
Small Caps{'\n'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Hoefler Text', fontVariant: ['oldstyle-nums']}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Times' : 'Hoefler Text'), fontVariant: ['oldstyle-nums']}}>
|
||||||
Old Style nums 0123456789{'\n'}
|
Old Style nums 0123456789{'\n'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontFamily: 'Hoefler Text', fontVariant: ['lining-nums']}}>
|
<Text style={{fontFamily: (Platform.isTVOS ? 'Times' : 'Hoefler Text'), fontVariant: ['lining-nums']}}>
|
||||||
Lining nums 0123456789{'\n'}
|
Lining nums 0123456789{'\n'}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={{fontVariant: ['tabular-nums']}}>
|
<Text style={{fontVariant: ['tabular-nums']}}>
|
||||||
|
@ -23,23 +23,22 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const ListView = require('ListView');
|
const ListView = require('ListView');
|
||||||
|
const Platform = require('Platform');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const StyleSheet = require('StyleSheet');
|
const StyleSheet = require('StyleSheet');
|
||||||
const Text = require('Text');
|
const Text = require('Text');
|
||||||
const TextInput = require('TextInput');
|
const TextInput = require('TextInput');
|
||||||
const TouchableHighlight = require('TouchableHighlight');
|
const TouchableHighlight = require('TouchableHighlight');
|
||||||
const View = require('View');
|
|
||||||
const UIExplorerActions = require('./UIExplorerActions');
|
const UIExplorerActions = require('./UIExplorerActions');
|
||||||
const UIExplorerStatePersister = require('./UIExplorerStatePersister');
|
const UIExplorerStatePersister = require('./UIExplorerStatePersister');
|
||||||
|
const View = require('View');
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
UIExplorerExample,
|
UIExplorerExample,
|
||||||
} from './UIExplorerList.ios';
|
} from './UIExplorerList.ios';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
PassProps,
|
PassProps,
|
||||||
} from './UIExplorerStatePersister';
|
} from './UIExplorerStatePersister';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
StyleObj,
|
StyleObj,
|
||||||
} from 'StyleSheetTypes';
|
} from 'StyleSheetTypes';
|
||||||
@ -66,7 +65,7 @@ class UIExplorerExampleList extends React.Component {
|
|||||||
render(): ?React.Element<any> {
|
render(): ?React.Element<any> {
|
||||||
const filterText = this.props.persister.state.filter;
|
const filterText = this.props.persister.state.filter;
|
||||||
const filterRegex = new RegExp(String(filterText), 'i');
|
const filterRegex = new RegExp(String(filterText), 'i');
|
||||||
const filter = (example) => filterRegex.test(example.module.title);
|
const filter = (example) => filterRegex.test(example.module.title) && (!Platform.isTVOS || example.supportsTVOS);
|
||||||
|
|
||||||
const dataSource = ds.cloneWithRowsAndSections({
|
const dataSource = ds.cloneWithRowsAndSections({
|
||||||
components: this.props.list.ComponentExamples.filter(filter),
|
components: this.props.list.ComponentExamples.filter(filter),
|
||||||
|
@ -25,132 +25,164 @@
|
|||||||
export type UIExplorerExample = {
|
export type UIExplorerExample = {
|
||||||
key: string,
|
key: string,
|
||||||
module: Object,
|
module: Object,
|
||||||
|
supportsTVOS: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
const ComponentExamples: Array<UIExplorerExample> = [
|
const ComponentExamples: Array<UIExplorerExample> = [
|
||||||
{
|
{
|
||||||
key: 'ActivityIndicatorExample',
|
key: 'ActivityIndicatorExample',
|
||||||
module: require('./ActivityIndicatorExample'),
|
module: require('./ActivityIndicatorExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ButtonExample',
|
key: 'ButtonExample',
|
||||||
module: require('./ButtonExample'),
|
module: require('./ButtonExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'DatePickerIOSExample',
|
key: 'DatePickerIOSExample',
|
||||||
module: require('./DatePickerIOSExample'),
|
module: require('./DatePickerIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ImageExample',
|
key: 'ImageExample',
|
||||||
module: require('./ImageExample'),
|
module: require('./ImageExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'KeyboardAvoidingViewExample',
|
key: 'KeyboardAvoidingViewExample',
|
||||||
module: require('./KeyboardAvoidingViewExample'),
|
module: require('./KeyboardAvoidingViewExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'LayoutEventsExample',
|
key: 'LayoutEventsExample',
|
||||||
module: require('./LayoutEventsExample'),
|
module: require('./LayoutEventsExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ListViewExample',
|
key: 'ListViewExample',
|
||||||
module: require('./ListViewExample'),
|
module: require('./ListViewExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ListViewGridLayoutExample',
|
key: 'ListViewGridLayoutExample',
|
||||||
module: require('./ListViewGridLayoutExample'),
|
module: require('./ListViewGridLayoutExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ListViewPagingExample',
|
key: 'ListViewPagingExample',
|
||||||
module: require('./ListViewPagingExample'),
|
module: require('./ListViewPagingExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'MapViewExample',
|
key: 'MapViewExample',
|
||||||
module: require('./MapViewExample'),
|
module: require('./MapViewExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ModalExample',
|
key: 'ModalExample',
|
||||||
module: require('./ModalExample'),
|
module: require('./ModalExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NavigatorExample',
|
key: 'NavigatorExample',
|
||||||
module: require('./Navigator/NavigatorExample'),
|
module: require('./Navigator/NavigatorExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NavigatorIOSColorsExample',
|
key: 'NavigatorIOSColorsExample',
|
||||||
module: require('./NavigatorIOSColorsExample'),
|
module: require('./NavigatorIOSColorsExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NavigatorIOSExample',
|
key: 'NavigatorIOSExample',
|
||||||
module: require('./NavigatorIOSExample'),
|
module: require('./NavigatorIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'PickerExample',
|
key: 'PickerExample',
|
||||||
module: require('./PickerExample'),
|
module: require('./PickerExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'PickerIOSExample',
|
key: 'PickerIOSExample',
|
||||||
module: require('./PickerIOSExample'),
|
module: require('./PickerIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ProgressViewIOSExample',
|
key: 'ProgressViewIOSExample',
|
||||||
module: require('./ProgressViewIOSExample'),
|
module: require('./ProgressViewIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'RefreshControlExample',
|
key: 'RefreshControlExample',
|
||||||
module: require('./RefreshControlExample'),
|
module: require('./RefreshControlExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ScrollViewExample',
|
key: 'ScrollViewExample',
|
||||||
module: require('./ScrollViewExample'),
|
module: require('./ScrollViewExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'SegmentedControlIOSExample',
|
key: 'SegmentedControlIOSExample',
|
||||||
module: require('./SegmentedControlIOSExample'),
|
module: require('./SegmentedControlIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'SliderExample',
|
key: 'SliderExample',
|
||||||
module: require('./SliderExample'),
|
module: require('./SliderExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'StatusBarExample',
|
key: 'StatusBarExample',
|
||||||
module: require('./StatusBarExample'),
|
module: require('./StatusBarExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'SwipeableListViewExample',
|
key: 'SwipeableListViewExample',
|
||||||
module: require('./SwipeableListViewExample')
|
module: require('./SwipeableListViewExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'SwitchExample',
|
key: 'SwitchExample',
|
||||||
module: require('./SwitchExample'),
|
module: require('./SwitchExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TabBarIOSExample',
|
key: 'TabBarIOSExample',
|
||||||
module: require('./TabBarIOSExample'),
|
module: require('./TabBarIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TextExample',
|
key: 'TextExample',
|
||||||
module: require('./TextExample.ios'),
|
module: require('./TextExample.ios'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TextInputExample',
|
key: 'TextInputExample',
|
||||||
module: require('./TextInputExample.ios'),
|
module: require('./TextInputExample.ios'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TouchableExample',
|
key: 'TouchableExample',
|
||||||
module: require('./TouchableExample'),
|
module: require('./TouchableExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TransparentHitTestExample',
|
key: 'TransparentHitTestExample',
|
||||||
module: require('./TransparentHitTestExample'),
|
module: require('./TransparentHitTestExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ViewExample',
|
key: 'ViewExample',
|
||||||
module: require('./ViewExample'),
|
module: require('./ViewExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'WebViewExample',
|
key: 'WebViewExample',
|
||||||
module: require('./WebViewExample'),
|
module: require('./WebViewExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -158,138 +190,172 @@ const APIExamples: Array<UIExplorerExample> = [
|
|||||||
{
|
{
|
||||||
key: 'AccessibilityIOSExample',
|
key: 'AccessibilityIOSExample',
|
||||||
module: require('./AccessibilityIOSExample'),
|
module: require('./AccessibilityIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ActionSheetIOSExample',
|
key: 'ActionSheetIOSExample',
|
||||||
module: require('./ActionSheetIOSExample'),
|
module: require('./ActionSheetIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AdSupportIOSExample',
|
key: 'AdSupportIOSExample',
|
||||||
module: require('./AdSupportIOSExample'),
|
module: require('./AdSupportIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AlertExample',
|
key: 'AlertExample',
|
||||||
module: require('./AlertExample').AlertExample,
|
module: require('./AlertExample').AlertExample,
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AlertIOSExample',
|
key: 'AlertIOSExample',
|
||||||
module: require('./AlertIOSExample'),
|
module: require('./AlertIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AnimatedExample',
|
key: 'AnimatedExample',
|
||||||
module: require('./AnimatedExample'),
|
module: require('./AnimatedExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AnExApp',
|
key: 'AnExApp',
|
||||||
module: require('./AnimatedGratuitousApp/AnExApp'),
|
module: require('./AnimatedGratuitousApp/AnExApp'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AppStateExample',
|
key: 'AppStateExample',
|
||||||
module: require('./AppStateExample'),
|
module: require('./AppStateExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'AsyncStorageExample',
|
key: 'AsyncStorageExample',
|
||||||
module: require('./AsyncStorageExample'),
|
module: require('./AsyncStorageExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'BorderExample',
|
key: 'BorderExample',
|
||||||
module: require('./BorderExample'),
|
module: require('./BorderExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'BoxShadowExample',
|
key: 'BoxShadowExample',
|
||||||
module: require('./BoxShadowExample'),
|
module: require('./BoxShadowExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'CameraRollExample',
|
key: 'CameraRollExample',
|
||||||
module: require('./CameraRollExample'),
|
module: require('./CameraRollExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ClipboardExample',
|
key: 'ClipboardExample',
|
||||||
module: require('./ClipboardExample'),
|
module: require('./ClipboardExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'GeolocationExample',
|
key: 'GeolocationExample',
|
||||||
module: require('./GeolocationExample'),
|
module: require('./GeolocationExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ImageEditingExample',
|
key: 'ImageEditingExample',
|
||||||
module: require('./ImageEditingExample'),
|
module: require('./ImageEditingExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'LayoutAnimationExample',
|
key: 'LayoutAnimationExample',
|
||||||
module: require('./LayoutAnimationExample'),
|
module: require('./LayoutAnimationExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'LayoutExample',
|
key: 'LayoutExample',
|
||||||
module: require('./LayoutExample'),
|
module: require('./LayoutExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'LinkingExample',
|
key: 'LinkingExample',
|
||||||
module: require('./LinkingExample'),
|
module: require('./LinkingExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NativeAnimationsExample',
|
key: 'NativeAnimationsExample',
|
||||||
module: require('./NativeAnimationsExample'),
|
module: require('./NativeAnimationsExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NavigationExperimentalExample',
|
key: 'NavigationExperimentalExample',
|
||||||
module: require('./NavigationExperimental/NavigationExperimentalExample'),
|
module: require('./NavigationExperimental/NavigationExperimentalExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'NetInfoExample',
|
key: 'NetInfoExample',
|
||||||
module: require('./NetInfoExample'),
|
module: require('./NetInfoExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'OrientationChangeExample',
|
key: 'OrientationChangeExample',
|
||||||
module: require('./OrientationChangeExample'),
|
module: require('./OrientationChangeExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'PanResponderExample',
|
key: 'PanResponderExample',
|
||||||
module: require('./PanResponderExample'),
|
module: require('./PanResponderExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'PointerEventsExample',
|
key: 'PointerEventsExample',
|
||||||
module: require('./PointerEventsExample'),
|
module: require('./PointerEventsExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'PushNotificationIOSExample',
|
key: 'PushNotificationIOSExample',
|
||||||
module: require('./PushNotificationIOSExample'),
|
module: require('./PushNotificationIOSExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'RCTRootViewIOSExample',
|
key: 'RCTRootViewIOSExample',
|
||||||
module: require('./RCTRootViewIOSExample'),
|
module: require('./RCTRootViewIOSExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'RTLExample',
|
key: 'RTLExample',
|
||||||
module: require('./RTLExample'),
|
module: require('./RTLExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'ShareExample',
|
key: 'ShareExample',
|
||||||
module: require('./ShareExample'),
|
module: require('./ShareExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'SnapshotExample',
|
key: 'SnapshotExample',
|
||||||
module: require('./SnapshotExample'),
|
module: require('./SnapshotExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TimerExample',
|
key: 'TimerExample',
|
||||||
module: require('./TimerExample'),
|
module: require('./TimerExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'TransformExample',
|
key: 'TransformExample',
|
||||||
module: require('./TransformExample'),
|
module: require('./TransformExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'VibrationExample',
|
key: 'VibrationExample',
|
||||||
module: require('./VibrationExample'),
|
module: require('./VibrationExample'),
|
||||||
|
supportsTVOS: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'WebSocketExample',
|
key: 'WebSocketExample',
|
||||||
module: require('./WebSocketExample'),
|
module: require('./WebSocketExample'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'XHRExample',
|
key: 'XHRExample',
|
||||||
module: require('./XHRExample.ios'),
|
module: require('./XHRExample.ios'),
|
||||||
|
supportsTVOS: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
41
Libraries/Components/AppleTV/TVEventHandler.android.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2016-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
|
||||||
|
* all intellectual property and other proprietary rights, in and to the React
|
||||||
|
* Native CustomComponents software (the "Software"). Subject to your
|
||||||
|
* compliance with these terms, you are hereby granted a non-exclusive,
|
||||||
|
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
||||||
|
* and (2) reproduce and distribute the Software as part of your own software
|
||||||
|
* ("Your Software"). Facebook reserves all rights not expressly granted to
|
||||||
|
* you in this license agreement.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
||||||
|
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @providesModule TVEventHandler
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function TVEventHandler() {}
|
||||||
|
|
||||||
|
TVEventHandler.prototype.enable = function(component: ?any, callback: Function) {};
|
||||||
|
|
||||||
|
TVEventHandler.prototype.disable = function() {};
|
||||||
|
|
||||||
|
module.exports = TVEventHandler;
|
70
Libraries/Components/AppleTV/TVEventHandler.ios.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2016-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* Facebook, Inc. ("Facebook") owns all right, title and interest, including
|
||||||
|
* all intellectual property and other proprietary rights, in and to the React
|
||||||
|
* Native CustomComponents software (the "Software"). Subject to your
|
||||||
|
* compliance with these terms, you are hereby granted a non-exclusive,
|
||||||
|
* worldwide, royalty-free copyright license to (1) use and copy the Software;
|
||||||
|
* and (2) reproduce and distribute the Software as part of your own software
|
||||||
|
* ("Your Software"). Facebook reserves all rights not expressly granted to
|
||||||
|
* you in this license agreement.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE AND DOCUMENTATION, IF ANY, ARE PROVIDED "AS IS" AND ANY EXPRESS
|
||||||
|
* OR IMPLIED WARRANTIES (INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE) ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL FACEBOOK OR ITS AFFILIATES, OFFICERS, DIRECTORS OR
|
||||||
|
* EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THE SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @providesModule TVEventHandler
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const React = require('React');
|
||||||
|
const TVNavigationEventEmitter = require('NativeModules').TVNavigationEventEmitter;
|
||||||
|
const NativeEventEmitter = require('NativeEventEmitter');
|
||||||
|
|
||||||
|
function TVEventHandler() {
|
||||||
|
this.__nativeTVNavigationEventListener = null;
|
||||||
|
this.__nativeTVNavigationEventEmitter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TVEventHandler.prototype.enable = function(component: ?any, callback: Function) {
|
||||||
|
if (!TVNavigationEventEmitter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__nativeTVNavigationEventEmitter = new NativeEventEmitter(TVNavigationEventEmitter);
|
||||||
|
this.__nativeTVNavigationEventListener = this.__nativeTVNavigationEventEmitter.addListener(
|
||||||
|
'onTVNavEvent',
|
||||||
|
(data) => {
|
||||||
|
if (callback) {
|
||||||
|
callback(component, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
TVEventHandler.prototype.disable = function() {
|
||||||
|
if (this.__nativeTVNavigationEventListener) {
|
||||||
|
this.__nativeTVNavigationEventListener.remove();
|
||||||
|
delete this.__nativeTVNavigationEventListener;
|
||||||
|
}
|
||||||
|
if (this.__nativeTVNavigationEventEmitter) {
|
||||||
|
delete this.__nativeTVNavigationEventEmitter;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TVEventHandler;
|
77
Libraries/Components/AppleTV/TVViewPropTypes.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule TVViewPropTypes
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
var PropTypes = require('React').PropTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional View properties for Apple TV
|
||||||
|
*/
|
||||||
|
var TVViewPropTypes = {
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* When set to true, this view will be focusable
|
||||||
|
* and navigable using the Apple TV remote.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
isTVSelectable: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* May be set to true to force the Apple TV focus engine to move focus to this view.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
hasTVPreferredFocus: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* Object with properties to control Apple TV parallax effects.
|
||||||
|
*
|
||||||
|
* enabled: If true, parallax effects are enabled. Defaults to true.
|
||||||
|
* shiftDistanceX: Defaults to 2.0.
|
||||||
|
* shiftDistanceY: Defaults to 2.0.
|
||||||
|
* tiltAngle: Defaults to 0.05.
|
||||||
|
* magnification: Defaults to 1.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxProperties: PropTypes.object,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* May be used to change the appearance of the Apple TV parallax effect when this view goes in or out of focus. Defaults to 2.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxShiftDistanceX: PropTypes.number,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* May be used to change the appearance of the Apple TV parallax effect when this view goes in or out of focus. Defaults to 2.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxShiftDistanceY: PropTypes.number,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* May be used to change the appearance of the Apple TV parallax effect when this view goes in or out of focus. Defaults to 0.05.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxTiltAngle: PropTypes.number,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* May be used to change the appearance of the Apple TV parallax effect when this view goes in or out of focus. Defaults to 1.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxMagnification: PropTypes.number,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TVViewPropTypes;
|
@ -19,6 +19,7 @@ var React = require('React');
|
|||||||
var ReactNative = require('ReactNative');
|
var ReactNative = require('ReactNative');
|
||||||
var StaticContainer = require('StaticContainer.react');
|
var StaticContainer = require('StaticContainer.react');
|
||||||
var StyleSheet = require('StyleSheet');
|
var StyleSheet = require('StyleSheet');
|
||||||
|
var TVEventHandler = require('TVEventHandler');
|
||||||
var View = require('View');
|
var View = require('View');
|
||||||
|
|
||||||
var invariant = require('fbjs/lib/invariant');
|
var invariant = require('fbjs/lib/invariant');
|
||||||
@ -37,13 +38,13 @@ function getuid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NavigatorTransitionerIOS extends React.Component {
|
class NavigatorTransitionerIOS extends React.Component {
|
||||||
requestSchedulingNavigation = (cb) => {
|
requestSchedulingNavigation(cb) {
|
||||||
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
|
RCTNavigatorManager.requestSchedulingJavaScriptNavigation(
|
||||||
ReactNative.findNodeHandle(this),
|
ReactNative.findNodeHandle(this),
|
||||||
logError,
|
logError,
|
||||||
cb
|
cb
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@ -89,11 +90,11 @@ type Route = {
|
|||||||
backButtonIcon?: Object,
|
backButtonIcon?: Object,
|
||||||
leftButtonTitle?: string,
|
leftButtonTitle?: string,
|
||||||
leftButtonIcon?: Object,
|
leftButtonIcon?: Object,
|
||||||
leftButtonSystemIcon?: SystemButtonType;
|
leftButtonSystemIcon?: SystemButtonType,
|
||||||
onLeftButtonPress?: Function,
|
onLeftButtonPress?: Function,
|
||||||
rightButtonTitle?: string,
|
rightButtonTitle?: string,
|
||||||
rightButtonIcon?: Object,
|
rightButtonIcon?: Object,
|
||||||
rightButtonSystemIcon?: SystemButtonType;
|
rightButtonSystemIcon?: SystemButtonType,
|
||||||
onRightButtonPress?: Function,
|
onRightButtonPress?: Function,
|
||||||
wrapperStyle?: any,
|
wrapperStyle?: any,
|
||||||
};
|
};
|
||||||
@ -519,11 +520,13 @@ var NavigatorIOS = React.createClass({
|
|||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._emitDidFocus(this.state.routeStack[this.state.observedTopOfStack]);
|
this._emitDidFocus(this.state.routeStack[this.state.observedTopOfStack]);
|
||||||
|
this._enableTVEventHandler();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
this.navigationContext.dispose();
|
this.navigationContext.dispose();
|
||||||
this.navigationContext = new NavigationContext();
|
this.navigationContext = new NavigationContext();
|
||||||
|
this._disableTVEventHandler();
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function(): Object {
|
getDefaultProps: function(): Object {
|
||||||
@ -891,6 +894,24 @@ var NavigatorIOS = React.createClass({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_tvEventHandler: (undefined: ?TVEventHandler),
|
||||||
|
|
||||||
|
_enableTVEventHandler: function() {
|
||||||
|
this._tvEventHandler = new TVEventHandler();
|
||||||
|
this._tvEventHandler.enable(this, function(cmp, evt) {
|
||||||
|
if (evt && evt.eventType === 'menu') {
|
||||||
|
cmp.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_disableTVEventHandler: function() {
|
||||||
|
if (this._tvEventHandler) {
|
||||||
|
this._tvEventHandler.disable();
|
||||||
|
delete this._tvEventHandler;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<View style={this.props.style}>
|
<View style={this.props.style}>
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var ColorPropType = require('ColorPropType');
|
||||||
var Image = require('Image');
|
var Image = require('Image');
|
||||||
var React = require('React');
|
var React = require('React');
|
||||||
var StaticContainer = require('StaticContainer.react');
|
var StaticContainer = require('StaticContainer.react');
|
||||||
var StyleSheet = require('StyleSheet');
|
var StyleSheet = require('StyleSheet');
|
||||||
var View = require('View');
|
var View = require('View');
|
||||||
var ColorPropType = require('ColorPropType');
|
|
||||||
|
|
||||||
var requireNativeComponent = require('requireNativeComponent');
|
var requireNativeComponent = require('requireNativeComponent');
|
||||||
|
|
||||||
@ -86,6 +86,13 @@ class TabBarItemIOS extends React.Component {
|
|||||||
* is defined.
|
* is defined.
|
||||||
*/
|
*/
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
|
/**
|
||||||
|
*(Apple TV only)* When set to true, this view will be focusable
|
||||||
|
* and navigable using the Apple TV remote.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
isTVSelectable: React.PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -12,12 +12,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const BoundingDimensions = require('BoundingDimensions');
|
const BoundingDimensions = require('BoundingDimensions');
|
||||||
|
const Platform = require('Platform');
|
||||||
const Position = require('Position');
|
const Position = require('Position');
|
||||||
const React = require('React'); // eslint-disable-line no-unused-vars
|
const React = require('React');
|
||||||
|
const TVEventHandler = require('TVEventHandler');
|
||||||
const TouchEventUtils = require('fbjs/lib/TouchEventUtils');
|
const TouchEventUtils = require('fbjs/lib/TouchEventUtils');
|
||||||
const UIManager = require('UIManager');
|
const UIManager = require('UIManager');
|
||||||
const View = require('View');
|
const View = require('View');
|
||||||
|
|
||||||
|
const findNodeHandle = require('findNodeHandle');
|
||||||
const keyMirror = require('fbjs/lib/keyMirror');
|
const keyMirror = require('fbjs/lib/keyMirror');
|
||||||
const normalizeColor = require('normalizeColor');
|
const normalizeColor = require('normalizeColor');
|
||||||
|
|
||||||
@ -313,10 +316,35 @@ var LONG_PRESS_ALLOWED_MOVEMENT = 10;
|
|||||||
* @lends Touchable.prototype
|
* @lends Touchable.prototype
|
||||||
*/
|
*/
|
||||||
var TouchableMixin = {
|
var TouchableMixin = {
|
||||||
|
componentDidMount: function() {
|
||||||
|
if (!Platform.isTVOS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tvEventHandler = new TVEventHandler();
|
||||||
|
this._tvEventHandler.enable(this, function(cmp, evt) {
|
||||||
|
var myTag = findNodeHandle(cmp);
|
||||||
|
evt.dispatchConfig = {};
|
||||||
|
if (myTag === evt.tag) {
|
||||||
|
if (evt.eventType === 'focus') {
|
||||||
|
cmp.touchableHandleActivePressIn && cmp.touchableHandleActivePressIn(evt);
|
||||||
|
} else if (evt.eventType === 'blur') {
|
||||||
|
cmp.touchableHandleActivePressOut && cmp.touchableHandleActivePressOut(evt);
|
||||||
|
} else if (evt.eventType === 'select') {
|
||||||
|
cmp.touchableHandlePress && cmp.touchableHandlePress(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all timeouts on unmount
|
* Clear all timeouts on unmount
|
||||||
*/
|
*/
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
|
if (this._tvEventHandler) {
|
||||||
|
this._tvEventHandler.disable();
|
||||||
|
delete this._tvEventHandler;
|
||||||
|
}
|
||||||
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
|
this.touchableDelayTimeout && clearTimeout(this.touchableDelayTimeout);
|
||||||
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
|
this.longPressDelayTimeout && clearTimeout(this.longPressDelayTimeout);
|
||||||
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
|
this.pressOutDelayTimeout && clearTimeout(this.pressOutDelayTimeout);
|
||||||
|
@ -86,6 +86,25 @@ var TouchableHighlight = React.createClass({
|
|||||||
* Called immediately after the underlay is hidden
|
* Called immediately after the underlay is hidden
|
||||||
*/
|
*/
|
||||||
onHideUnderlay: React.PropTypes.func,
|
onHideUnderlay: React.PropTypes.func,
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* TV preferred focus (see documentation for the View component).
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
hasTVPreferredFocus: React.PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* *(Apple TV only)* Object with properties to control Apple TV parallax effects.
|
||||||
|
*
|
||||||
|
* enabled: If true, parallax effects are enabled. Defaults to true.
|
||||||
|
* shiftDistanceX: Defaults to 2.0.
|
||||||
|
* shiftDistanceY: Defaults to 2.0.
|
||||||
|
* tiltAngle: Defaults to 0.05.
|
||||||
|
* magnification: Defaults to 1.0.
|
||||||
|
*
|
||||||
|
* @platform ios
|
||||||
|
*/
|
||||||
|
tvParallaxProperties: React.PropTypes.object,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
mixins: [NativeMethodsMixin, TimerMixin, Touchable.Mixin],
|
||||||
@ -108,7 +127,8 @@ var TouchableHighlight = React.createClass({
|
|||||||
underlayStyle: [
|
underlayStyle: [
|
||||||
INACTIVE_UNDERLAY_PROPS.style,
|
INACTIVE_UNDERLAY_PROPS.style,
|
||||||
props.style,
|
props.style,
|
||||||
]
|
],
|
||||||
|
hasTVPreferredFocus: props.hasTVPreferredFocus
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -234,6 +254,9 @@ var TouchableHighlight = React.createClass({
|
|||||||
style={this.state.underlayStyle}
|
style={this.state.underlayStyle}
|
||||||
onLayout={this.props.onLayout}
|
onLayout={this.props.onLayout}
|
||||||
hitSlop={this.props.hitSlop}
|
hitSlop={this.props.hitSlop}
|
||||||
|
isTVSelectable={true}
|
||||||
|
tvParallaxProperties={this.props.tvParallaxProperties}
|
||||||
|
hasTVPreferredFocus={this.state.hasTVPreferredFocus}
|
||||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||||
onResponderGrant={this.touchableHandleResponderGrant}
|
onResponderGrant={this.touchableHandleResponderGrant}
|
||||||
|
@ -59,11 +59,17 @@ var TouchableOpacity = React.createClass({
|
|||||||
* active. Defaults to 0.2.
|
* active. Defaults to 0.2.
|
||||||
*/
|
*/
|
||||||
activeOpacity: React.PropTypes.number,
|
activeOpacity: React.PropTypes.number,
|
||||||
|
focusedOpacity: React.PropTypes.number,
|
||||||
|
/**
|
||||||
|
* Apple TV parallax effects
|
||||||
|
*/
|
||||||
|
tvParallaxProperties: React.PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
activeOpacity: 0.2,
|
activeOpacity: 0.2,
|
||||||
|
focusedOpacity: 0.7,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -156,6 +162,10 @@ var TouchableOpacity = React.createClass({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_opacityFocused: function() {
|
||||||
|
this.setOpacityTo(this.props.focusedOpacity);
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
@ -166,6 +176,8 @@ var TouchableOpacity = React.createClass({
|
|||||||
style={[this.props.style, {opacity: this.state.anim}]}
|
style={[this.props.style, {opacity: this.state.anim}]}
|
||||||
testID={this.props.testID}
|
testID={this.props.testID}
|
||||||
onLayout={this.props.onLayout}
|
onLayout={this.props.onLayout}
|
||||||
|
isTVSelectable={true}
|
||||||
|
tvParallaxProperties={this.props.tvParallaxProperties}
|
||||||
hitSlop={this.props.hitSlop}
|
hitSlop={this.props.hitSlop}
|
||||||
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder}
|
||||||
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest}
|
||||||
|
@ -4,7 +4,9 @@ exports[`TouchableHighlight renders correctly 1`] = `
|
|||||||
accessibilityLabel={undefined}
|
accessibilityLabel={undefined}
|
||||||
accessibilityTraits={undefined}
|
accessibilityTraits={undefined}
|
||||||
accessible={true}
|
accessible={true}
|
||||||
|
hasTVPreferredFocus={undefined}
|
||||||
hitSlop={undefined}
|
hitSlop={undefined}
|
||||||
|
isTVSelectable={true}
|
||||||
onLayout={undefined}
|
onLayout={undefined}
|
||||||
onResponderGrant={[Function]}
|
onResponderGrant={[Function]}
|
||||||
onResponderMove={[Function]}
|
onResponderMove={[Function]}
|
||||||
@ -20,7 +22,8 @@ exports[`TouchableHighlight renders correctly 1`] = `
|
|||||||
Object {},
|
Object {},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
testID={undefined}>
|
testID={undefined}
|
||||||
|
tvParallaxProperties={undefined}>
|
||||||
<Text
|
<Text
|
||||||
accessible={true}
|
accessible={true}
|
||||||
allowFontScaling={true}
|
allowFontScaling={true}
|
||||||
|
@ -14,12 +14,18 @@
|
|||||||
const EdgeInsetsPropType = require('EdgeInsetsPropType');
|
const EdgeInsetsPropType = require('EdgeInsetsPropType');
|
||||||
const NativeMethodsMixin = require('NativeMethodsMixin');
|
const NativeMethodsMixin = require('NativeMethodsMixin');
|
||||||
const NativeModules = require('NativeModules');
|
const NativeModules = require('NativeModules');
|
||||||
|
const Platform = require('Platform');
|
||||||
const React = require('React');
|
const React = require('React');
|
||||||
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
|
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
|
||||||
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
|
||||||
const StyleSheetPropType = require('StyleSheetPropType');
|
const StyleSheetPropType = require('StyleSheetPropType');
|
||||||
const ViewStylePropTypes = require('ViewStylePropTypes');
|
const ViewStylePropTypes = require('ViewStylePropTypes');
|
||||||
|
|
||||||
|
var TVViewPropTypes = {};
|
||||||
|
if (Platform.isTVOS) {
|
||||||
|
TVViewPropTypes = require('TVViewPropTypes');
|
||||||
|
}
|
||||||
|
|
||||||
const requireNativeComponent = require('requireNativeComponent');
|
const requireNativeComponent = require('requireNativeComponent');
|
||||||
|
|
||||||
const PropTypes = React.PropTypes;
|
const PropTypes = React.PropTypes;
|
||||||
@ -133,6 +139,8 @@ const View = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
...TVViewPropTypes,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When `true`, indicates that the view is an accessibility element. By default,
|
* When `true`, indicates that the view is an accessibility element. By default,
|
||||||
* all the touchable elements are accessible.
|
* all the touchable elements are accessible.
|
||||||
|
@ -32,13 +32,14 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('React');
|
|
||||||
const ReactNative = require('react-native');
|
|
||||||
const NavigationHeaderTitle = require('NavigationHeaderTitle');
|
|
||||||
const NavigationHeaderBackButton = require('NavigationHeaderBackButton');
|
const NavigationHeaderBackButton = require('NavigationHeaderBackButton');
|
||||||
const NavigationPropTypes = require('NavigationPropTypes');
|
|
||||||
const NavigationHeaderStyleInterpolator = require('NavigationHeaderStyleInterpolator');
|
const NavigationHeaderStyleInterpolator = require('NavigationHeaderStyleInterpolator');
|
||||||
|
const NavigationHeaderTitle = require('NavigationHeaderTitle');
|
||||||
|
const NavigationPropTypes = require('NavigationPropTypes');
|
||||||
|
const React = require('React');
|
||||||
const ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin');
|
const ReactComponentWithPureRenderMixin = require('react/lib/ReactComponentWithPureRenderMixin');
|
||||||
|
const ReactNative = require('react-native');
|
||||||
|
const TVEventHandler = require('TVEventHandler');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
Animated,
|
Animated,
|
||||||
@ -128,6 +129,24 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_tvEventHandler: TVEventHandler;
|
||||||
|
|
||||||
|
componentDidMount(): void {
|
||||||
|
this._tvEventHandler = new TVEventHandler();
|
||||||
|
this._tvEventHandler.enable(this, function(cmp, evt) {
|
||||||
|
if (evt && evt.eventType === 'menu') {
|
||||||
|
cmp.props.onNavigateBack && cmp.props.onNavigateBack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
if (this._tvEventHandler) {
|
||||||
|
this._tvEventHandler.disable();
|
||||||
|
delete this._tvEventHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(): React.Element<any> {
|
render(): React.Element<any> {
|
||||||
const { scenes, style, viewProps } = this.props;
|
const { scenes, style, viewProps } = this.props;
|
||||||
|
|
||||||
@ -156,32 +175,32 @@ class NavigationHeader extends React.Component<DefaultProps, Props, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderLeft(props: NavigationSceneRendererProps): ?React.Element<any> {
|
_renderLeft = (props: NavigationSceneRendererProps): ?React.Element<any> => {
|
||||||
return this._renderSubView(
|
return this._renderSubView(
|
||||||
props,
|
props,
|
||||||
'left',
|
'left',
|
||||||
this.props.renderLeftComponent,
|
this.props.renderLeftComponent,
|
||||||
NavigationHeaderStyleInterpolator.forLeft,
|
NavigationHeaderStyleInterpolator.forLeft,
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
_renderTitle(props: NavigationSceneRendererProps): ?React.Element<any> {
|
_renderTitle = (props: NavigationSceneRendererProps): ?React.Element<any> => {
|
||||||
return this._renderSubView(
|
return this._renderSubView(
|
||||||
props,
|
props,
|
||||||
'title',
|
'title',
|
||||||
this.props.renderTitleComponent,
|
this.props.renderTitleComponent,
|
||||||
NavigationHeaderStyleInterpolator.forCenter,
|
NavigationHeaderStyleInterpolator.forCenter,
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
_renderRight(props: NavigationSceneRendererProps): ?React.Element<any> {
|
_renderRight = (props: NavigationSceneRendererProps): ?React.Element<any> => {
|
||||||
return this._renderSubView(
|
return this._renderSubView(
|
||||||
props,
|
props,
|
||||||
'right',
|
'right',
|
||||||
this.props.renderRightComponent,
|
this.props.renderRightComponent,
|
||||||
NavigationHeaderStyleInterpolator.forRight,
|
NavigationHeaderStyleInterpolator.forRight,
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
_renderSubView(
|
_renderSubView(
|
||||||
props: NavigationSceneRendererProps,
|
props: NavigationSceneRendererProps,
|
||||||
|
@ -43,6 +43,7 @@ var PanResponder = require('PanResponder');
|
|||||||
var React = require('React');
|
var React = require('React');
|
||||||
var StyleSheet = require('StyleSheet');
|
var StyleSheet = require('StyleSheet');
|
||||||
var Subscribable = require('Subscribable');
|
var Subscribable = require('Subscribable');
|
||||||
|
var TVEventHandler = require('TVEventHandler');
|
||||||
var TimerMixin = require('react-timer-mixin');
|
var TimerMixin = require('react-timer-mixin');
|
||||||
var View = require('View');
|
var View = require('View');
|
||||||
|
|
||||||
@ -472,6 +473,7 @@ var Navigator = React.createClass({
|
|||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this._handleSpringUpdate();
|
this._handleSpringUpdate();
|
||||||
this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]);
|
this._emitDidFocus(this.state.routeStack[this.state.presentedIndex]);
|
||||||
|
this._enableTVEventHandler();
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
@ -485,6 +487,8 @@ var Navigator = React.createClass({
|
|||||||
if (this._interactionHandle) {
|
if (this._interactionHandle) {
|
||||||
this.clearInteractionHandle(this._interactionHandle);
|
this.clearInteractionHandle(this._interactionHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._disableTVEventHandler();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1302,6 +1306,24 @@ var Navigator = React.createClass({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_tvEventHandler: TVEventHandler,
|
||||||
|
|
||||||
|
_enableTVEventHandler: function() {
|
||||||
|
this._tvEventHandler = new TVEventHandler();
|
||||||
|
this._tvEventHandler.enable(this, function(cmp, evt) {
|
||||||
|
if (evt && evt.eventType === 'menu') {
|
||||||
|
cmp.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_disableTVEventHandler: function() {
|
||||||
|
if (this._tvEventHandler) {
|
||||||
|
this._tvEventHandler.disable();
|
||||||
|
delete this._tvEventHandler;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var newRenderedSceneMap = new Map();
|
var newRenderedSceneMap = new Map();
|
||||||
var scenes = this.state.routeStack.map((route, index) => {
|
var scenes = this.state.routeStack.map((route, index) => {
|
||||||
|
@ -112,7 +112,11 @@ expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
|||||||
launchOptions:nil];
|
launchOptions:nil];
|
||||||
|
|
||||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
rootView.frame = CGRectMake(0, 0, 1920, 1080); // Standard screen size for tvOS
|
||||||
|
#else
|
||||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||||
|
#endif
|
||||||
|
|
||||||
RCTTestModule *testModule = [rootView.bridge moduleForClass:[RCTTestModule class]];
|
RCTTestModule *testModule = [rootView.bridge moduleForClass:[RCTTestModule class]];
|
||||||
RCTAssert(_testController != nil, @"_testController should not be nil");
|
RCTAssert(_testController != nil, @"_testController should not be nil");
|
||||||
|
@ -57,6 +57,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||||||
eventCount:_nativeEventCount];
|
eventCount:_nativeEventCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
||||||
|
{
|
||||||
|
[super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
|
||||||
|
if(context.nextFocusedView == self) {
|
||||||
|
_jsRequestingFirstResponder = YES;
|
||||||
|
} else {
|
||||||
|
_jsRequestingFirstResponder = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This method is overridden for `onKeyPress`. The manager
|
// This method is overridden for `onKeyPress`. The manager
|
||||||
// will not send a keyPress for text that was pasted.
|
// will not send a keyPress for text that was pasted.
|
||||||
- (void)paste:(id)sender
|
- (void)paste:(id)sender
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
#import <React/RCTFont.h>
|
#import <React/RCTFont.h>
|
||||||
#import <React/RCTShadowView.h>
|
#import <React/RCTShadowView.h>
|
||||||
|
|
||||||
#import "RCTTextView.h"
|
|
||||||
#import "RCTConvert+Text.h"
|
#import "RCTConvert+Text.h"
|
||||||
|
#import "RCTTextView.h"
|
||||||
|
|
||||||
@implementation RCTTextViewManager
|
@implementation RCTTextViewManager
|
||||||
|
|
||||||
@ -68,7 +68,10 @@ RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextView)
|
|||||||
view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName];
|
view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName];
|
||||||
}
|
}
|
||||||
RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
|
RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV
|
||||||
RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, textView.dataDetectorTypes, UIDataDetectorTypes)
|
RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, textView.dataDetectorTypes, UIDataDetectorTypes)
|
||||||
|
#endif
|
||||||
|
|
||||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Platform = {
|
const Platform = {
|
||||||
OS: 'android',
|
OS: 'android',
|
||||||
get Version() {
|
get Version() {
|
||||||
const AndroidConstants = require('NativeModules').AndroidConstants;
|
const AndroidConstants = require('NativeModules').AndroidConstants;
|
||||||
|
@ -12,10 +12,15 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Platform = {
|
const Platform = {
|
||||||
OS: 'ios',
|
OS: 'ios',
|
||||||
get Version() {
|
get Version() {
|
||||||
return require('NativeModules').IOSConstants.osVersion;
|
const constants = require('NativeModules').IOSConstants;
|
||||||
|
return constants ? constants.osVersion : '';
|
||||||
|
},
|
||||||
|
get isTVOS() {
|
||||||
|
const constants = require('NativeModules').IOSConstants;
|
||||||
|
return constants ? (constants.interfaceIdiom === 'tv') : false;
|
||||||
},
|
},
|
||||||
select: (obj: Object) => obj.ios,
|
select: (obj: Object) => obj.ios,
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,7 @@ RCT_EXPORT_MODULE(IOSConstants)
|
|||||||
return @{
|
return @{
|
||||||
@"forceTouchAvailable": @(RCTForceTouchAvailable()),
|
@"forceTouchAvailable": @(RCTForceTouchAvailable()),
|
||||||
@"osVersion": [device systemVersion],
|
@"osVersion": [device systemVersion],
|
||||||
|
@"systemName": [device systemName],
|
||||||
@"interfaceIdiom": interfaceIdiom([device userInterfaceIdiom]),
|
@"interfaceIdiom": interfaceIdiom([device userInterfaceIdiom]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,11 @@
|
|||||||
#import "UIView+React.h"
|
#import "UIView+React.h"
|
||||||
#import "RCTProfile.h"
|
#import "RCTProfile.h"
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
#import "RCTTVRemoteHandler.h"
|
||||||
|
#import "RCTTVNavigationEventEmitter.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotification";
|
NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotification";
|
||||||
|
|
||||||
@interface RCTUIManager (RCTRootView)
|
@interface RCTUIManager (RCTRootView)
|
||||||
@ -92,6 +97,13 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat
|
|||||||
name:RCTContentDidAppearNotification
|
name:RCTContentDidAppearNotification
|
||||||
object:self];
|
object:self];
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
self.tvRemoteHandler = [RCTTVRemoteHandler new];
|
||||||
|
for (UIGestureRecognizer *gr in self.tvRemoteHandler.tvRemoteGestureRecognizers) {
|
||||||
|
[self addGestureRecognizer:gr];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!_bridge.loading) {
|
if (!_bridge.loading) {
|
||||||
[self bundleFinishedLoading:[_bridge batchedBridge]];
|
[self bundleFinishedLoading:[_bridge batchedBridge]];
|
||||||
}
|
}
|
||||||
@ -119,6 +131,16 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat
|
|||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
- (UIView *)preferredFocusedView
|
||||||
|
{
|
||||||
|
if (self.reactPreferredFocusedView) {
|
||||||
|
return self.reactPreferredFocusedView;
|
||||||
|
}
|
||||||
|
return [super preferredFocusedView];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||||
{
|
{
|
||||||
super.backgroundColor = backgroundColor;
|
super.backgroundColor = backgroundColor;
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
#import <React/RCTRootView.h>
|
#import <React/RCTRootView.h>
|
||||||
|
|
||||||
|
@class RCTTVRemoteHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface provides a set of functions that allow other internal framework
|
* The interface provides a set of functions that allow other internal framework
|
||||||
* classes to change the RCTRootViews's internal state.
|
* classes to change the RCTRootViews's internal state.
|
||||||
@ -20,4 +22,12 @@
|
|||||||
*/
|
*/
|
||||||
@property (readwrite, nonatomic, assign) CGSize intrinsicSize;
|
@property (readwrite, nonatomic, assign) CGSize intrinsicSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV remote gesture recognizers
|
||||||
|
*/
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler;
|
||||||
|
@property (nonatomic, strong) UIView *reactPreferredFocusedView;
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
16
React/Base/RCTTVRemoteHandler.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface RCTTVRemoteHandler : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, copy, readonly) NSArray *tvRemoteGestureRecognizers;
|
||||||
|
|
||||||
|
@end
|
153
React/Base/RCTTVRemoteHandler.m
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTTVRemoteHandler.h"
|
||||||
|
|
||||||
|
#import <UIKit/UIGestureRecognizerSubclass.h>
|
||||||
|
|
||||||
|
#import "RCTAssert.h"
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTRootView.h"
|
||||||
|
#import "RCTTVNavigationEventEmitter.h"
|
||||||
|
#import "RCTUIManager.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTView.h"
|
||||||
|
#import "UIView+React.h"
|
||||||
|
|
||||||
|
@implementation RCTTVRemoteHandler {
|
||||||
|
NSMutableArray<UIGestureRecognizer *> *_tvRemoteGestureRecognizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_tvRemoteGestureRecognizers = [NSMutableArray array];
|
||||||
|
|
||||||
|
// Recognizers for Apple TV remote buttons
|
||||||
|
|
||||||
|
// Play/Pause
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(playPausePressed:)
|
||||||
|
pressType:UIPressTypePlayPause];
|
||||||
|
|
||||||
|
// Menu
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(menuPressed:)
|
||||||
|
pressType:UIPressTypeMenu];
|
||||||
|
|
||||||
|
// Select
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(selectPressed:)
|
||||||
|
pressType:UIPressTypeSelect];
|
||||||
|
|
||||||
|
// Up
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(swipedUp:)
|
||||||
|
pressType:UIPressTypeUpArrow];
|
||||||
|
|
||||||
|
// Down
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(swipedDown:)
|
||||||
|
pressType:UIPressTypeDownArrow];
|
||||||
|
|
||||||
|
// Left
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(swipedLeft:)
|
||||||
|
pressType:UIPressTypeLeftArrow];
|
||||||
|
|
||||||
|
// Right
|
||||||
|
[self addTapGestureRecognizerWithSelector:@selector(swipedRight:)
|
||||||
|
pressType:UIPressTypeRightArrow];
|
||||||
|
|
||||||
|
|
||||||
|
// Recognizers for Apple TV remote trackpad swipes
|
||||||
|
|
||||||
|
// Up
|
||||||
|
[self addSwipeGestureRecognizerWithSelector:@selector(swipedUp:)
|
||||||
|
direction:UISwipeGestureRecognizerDirectionUp];
|
||||||
|
|
||||||
|
// Down
|
||||||
|
[self addSwipeGestureRecognizerWithSelector:@selector(swipedDown:)
|
||||||
|
direction:UISwipeGestureRecognizerDirectionDown];
|
||||||
|
|
||||||
|
// Left
|
||||||
|
[self addSwipeGestureRecognizerWithSelector:@selector(swipedLeft:)
|
||||||
|
direction:UISwipeGestureRecognizerDirectionLeft];
|
||||||
|
|
||||||
|
// Right
|
||||||
|
[self addSwipeGestureRecognizerWithSelector:@selector(swipedRight:)
|
||||||
|
direction:UISwipeGestureRecognizerDirectionRight];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)playPausePressed:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"playPause" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)menuPressed:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"menu" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)selectPressed:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"select" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)longPress:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"longPress" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)swipedUp:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"up" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)swipedDown:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"down" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)swipedLeft:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"left" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)swipedRight:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[self sendAppleTVEvent:@"right" toView:r.view];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
- (void)addTapGestureRecognizerWithSelector:(nonnull SEL)selector pressType:(UIPressType)pressType
|
||||||
|
{
|
||||||
|
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:selector];
|
||||||
|
recognizer.allowedPressTypes = @[@(pressType)];
|
||||||
|
|
||||||
|
[_tvRemoteGestureRecognizers addObject:recognizer];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addSwipeGestureRecognizerWithSelector:(nonnull SEL)selector direction:(UISwipeGestureRecognizerDirection)direction
|
||||||
|
{
|
||||||
|
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:selector];
|
||||||
|
recognizer.direction = direction;
|
||||||
|
|
||||||
|
[_tvRemoteGestureRecognizers addObject:recognizer];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)sendAppleTVEvent:(NSString *)eventType toView:(UIView *)v
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
|
||||||
|
object:@{@"eventType":eventType}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
16
React/Modules/RCTTVNavigationEventEmitter.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
|
||||||
|
RCT_EXTERN NSString *const RCTTVNavigationEventNotification;
|
||||||
|
|
||||||
|
@interface RCTTVNavigationEventEmitter : RCTEventEmitter
|
||||||
|
|
||||||
|
@end
|
47
React/Modules/RCTTVNavigationEventEmitter.m
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTTVNavigationEventEmitter.h"
|
||||||
|
|
||||||
|
NSString *const RCTTVNavigationEventNotification = @"RCTTVNavigationEventNotification";
|
||||||
|
|
||||||
|
static NSString *const TVNavigationEventName = @"onTVNavEvent";
|
||||||
|
|
||||||
|
@implementation RCTTVNavigationEventEmitter
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||||
|
selector:@selector(handleTVNavigationEventNotification:)
|
||||||
|
name:RCTTVNavigationEventNotification
|
||||||
|
object:nil];
|
||||||
|
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)supportedEvents
|
||||||
|
{
|
||||||
|
return @[TVNavigationEventName];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleTVNavigationEventNotification:(NSNotification *)notif
|
||||||
|
{
|
||||||
|
[self sendEventWithName:TVNavigationEventName body:notif.object];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -163,6 +163,7 @@
|
|||||||
2D3B5EF11D9B09E700451313 /* UIView+React.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+React.m */; };
|
2D3B5EF11D9B09E700451313 /* UIView+React.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+React.m */; };
|
||||||
2D74EAFA1DAE9590003B751B /* RCTMultipartDataTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 006FC4131D9B20820057AAAD /* RCTMultipartDataTask.m */; };
|
2D74EAFA1DAE9590003B751B /* RCTMultipartDataTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 006FC4131D9B20820057AAAD /* RCTMultipartDataTask.m */; };
|
||||||
2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 001BFCCF1D8381DE008E587E /* RCTMultipartStreamReader.m */; };
|
2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 001BFCCF1D8381DE008E587E /* RCTMultipartStreamReader.m */; };
|
||||||
|
2D9F8B9B1DE398DB00A16144 /* RCTPlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7749431DC1065C007EC8D8 /* RCTPlatform.m */; };
|
||||||
2DD0EFE11DA84F2800B0C975 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; };
|
2DD0EFE11DA84F2800B0C975 /* RCTStatusBarManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */; };
|
||||||
352DCFF01D19F4C20056D623 /* RCTI18nUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DCFEF1D19F4C20056D623 /* RCTI18nUtil.m */; };
|
352DCFF01D19F4C20056D623 /* RCTI18nUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DCFEF1D19F4C20056D623 /* RCTI18nUtil.m */; };
|
||||||
369123E11DDC75850095B341 /* JSCSamplingProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 369123E01DDC75850095B341 /* JSCSamplingProfiler.m */; };
|
369123E11DDC75850095B341 /* JSCSamplingProfiler.m in Sources */ = {isa = PBXBuildFile; fileRef = 369123E01DDC75850095B341 /* JSCSamplingProfiler.m */; };
|
||||||
@ -420,6 +421,15 @@
|
|||||||
3D3CD9441DE5FC6500167DC4 /* libjschelpers.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D3CD9181DE5FBD800167DC4 /* libjschelpers.a */; };
|
3D3CD9441DE5FC6500167DC4 /* libjschelpers.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D3CD9181DE5FBD800167DC4 /* libjschelpers.a */; };
|
||||||
3D3CD9451DE5FC7100167DC4 /* JSBundleType.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D3CD8F51DE5FB2300167DC4 /* JSBundleType.h */; };
|
3D3CD9451DE5FC7100167DC4 /* JSBundleType.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D3CD8F51DE5FB2300167DC4 /* JSBundleType.h */; };
|
||||||
3D3CD9471DE5FC7800167DC4 /* oss-compat-util.h in Headers */ = {isa = PBXBuildFile; fileRef = AC70D2EE1DE48AC5002E6351 /* oss-compat-util.h */; };
|
3D3CD9471DE5FC7800167DC4 /* oss-compat-util.h in Headers */ = {isa = PBXBuildFile; fileRef = AC70D2EE1DE48AC5002E6351 /* oss-compat-util.h */; };
|
||||||
|
3D5AC7131E0056C4000F9153 /* RCTTVView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC70F1E0056BC000F9153 /* RCTTVView.h */; };
|
||||||
|
3D5AC7141E0056C7000F9153 /* RCTTVView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5AC7101E0056BC000F9153 /* RCTTVView.m */; };
|
||||||
|
3D5AC7191E0056E0000F9153 /* RCTTVNavigationEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC7151E0056D9000F9153 /* RCTTVNavigationEventEmitter.h */; };
|
||||||
|
3D5AC71A1E0056E0000F9153 /* RCTTVNavigationEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5AC7161E0056D9000F9153 /* RCTTVNavigationEventEmitter.m */; };
|
||||||
|
3D5AC71B1E005723000F9153 /* RCTReloadCommand.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A2440AA01DF8D854006E7BFC /* RCTReloadCommand.h */; };
|
||||||
|
3D5AC71C1E005723000F9153 /* RCTTVNavigationEventEmitter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC7151E0056D9000F9153 /* RCTTVNavigationEventEmitter.h */; };
|
||||||
|
3D5AC71D1E00572F000F9153 /* RCTTVView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC70F1E0056BC000F9153 /* RCTTVView.h */; };
|
||||||
|
3D5AC7221E005763000F9153 /* RCTTVRemoteHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 3D5AC71E1E005750000F9153 /* RCTTVRemoteHandler.h */; };
|
||||||
|
3D5AC7231E005766000F9153 /* RCTTVRemoteHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D5AC71F1E005750000F9153 /* RCTTVRemoteHandler.m */; };
|
||||||
3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7749431DC1065C007EC8D8 /* RCTPlatform.m */; };
|
3D7749441DC1065C007EC8D8 /* RCTPlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7749431DC1065C007EC8D8 /* RCTPlatform.m */; };
|
||||||
3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; };
|
3D7A27E21DE325B7002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; };
|
||||||
3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; };
|
3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3D7A27E11DE325B7002E3F95 /* RCTJSCErrorHandling.mm */; };
|
||||||
@ -775,6 +785,9 @@
|
|||||||
dstPath = include/React;
|
dstPath = include/React;
|
||||||
dstSubfolderSpec = 16;
|
dstSubfolderSpec = 16;
|
||||||
files = (
|
files = (
|
||||||
|
3D5AC71D1E00572F000F9153 /* RCTTVView.h in Copy Headers */,
|
||||||
|
3D5AC71B1E005723000F9153 /* RCTReloadCommand.h in Copy Headers */,
|
||||||
|
3D5AC71C1E005723000F9153 /* RCTTVNavigationEventEmitter.h in Copy Headers */,
|
||||||
3D302FA01DF8290600D6DDAE /* RCTImageLoader.h in Copy Headers */,
|
3D302FA01DF8290600D6DDAE /* RCTImageLoader.h in Copy Headers */,
|
||||||
3D302FA11DF8290600D6DDAE /* RCTImageStoreManager.h in Copy Headers */,
|
3D302FA11DF8290600D6DDAE /* RCTImageStoreManager.h in Copy Headers */,
|
||||||
3D302FA21DF8290600D6DDAE /* RCTResizeMode.h in Copy Headers */,
|
3D302FA21DF8290600D6DDAE /* RCTResizeMode.h in Copy Headers */,
|
||||||
@ -1283,6 +1296,12 @@
|
|||||||
3D3CD9181DE5FBD800167DC4 /* libjschelpers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjschelpers.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
3D3CD9181DE5FBD800167DC4 /* libjschelpers.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjschelpers.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3D3CD9251DE5FBEC00167DC4 /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
3D3CD9251DE5FBEC00167DC4 /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
3D3CD9321DE5FBEE00167DC4 /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
3D3CD9321DE5FBEE00167DC4 /* libcxxreact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libcxxreact.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
3D5AC70F1E0056BC000F9153 /* RCTTVView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTVView.h; sourceTree = "<group>"; };
|
||||||
|
3D5AC7101E0056BC000F9153 /* RCTTVView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTVView.m; sourceTree = "<group>"; };
|
||||||
|
3D5AC7151E0056D9000F9153 /* RCTTVNavigationEventEmitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTVNavigationEventEmitter.h; sourceTree = "<group>"; };
|
||||||
|
3D5AC7161E0056D9000F9153 /* RCTTVNavigationEventEmitter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTVNavigationEventEmitter.m; sourceTree = "<group>"; };
|
||||||
|
3D5AC71E1E005750000F9153 /* RCTTVRemoteHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTVRemoteHandler.h; sourceTree = "<group>"; };
|
||||||
|
3D5AC71F1E005750000F9153 /* RCTTVRemoteHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTVRemoteHandler.m; sourceTree = "<group>"; };
|
||||||
3D7749421DC1065C007EC8D8 /* RCTPlatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPlatform.h; sourceTree = "<group>"; };
|
3D7749421DC1065C007EC8D8 /* RCTPlatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPlatform.h; sourceTree = "<group>"; };
|
||||||
3D7749431DC1065C007EC8D8 /* RCTPlatform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPlatform.m; sourceTree = "<group>"; };
|
3D7749431DC1065C007EC8D8 /* RCTPlatform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPlatform.m; sourceTree = "<group>"; };
|
||||||
3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCore.h; sourceTree = "<group>"; };
|
3D7A27DC1DE32541002E3F95 /* JavaScriptCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCore.h; sourceTree = "<group>"; };
|
||||||
@ -1433,6 +1452,8 @@
|
|||||||
13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */,
|
13723B4F1A82FD3C00F88898 /* RCTStatusBarManager.m */,
|
||||||
13B07FED1A69327A00A75B9A /* RCTTiming.h */,
|
13B07FED1A69327A00A75B9A /* RCTTiming.h */,
|
||||||
13B07FEE1A69327A00A75B9A /* RCTTiming.m */,
|
13B07FEE1A69327A00A75B9A /* RCTTiming.m */,
|
||||||
|
3D5AC7151E0056D9000F9153 /* RCTTVNavigationEventEmitter.h */,
|
||||||
|
3D5AC7161E0056D9000F9153 /* RCTTVNavigationEventEmitter.m */,
|
||||||
13E067481A70F434002CDEE1 /* RCTUIManager.h */,
|
13E067481A70F434002CDEE1 /* RCTUIManager.h */,
|
||||||
13E067491A70F434002CDEE1 /* RCTUIManager.m */,
|
13E067491A70F434002CDEE1 /* RCTUIManager.m */,
|
||||||
);
|
);
|
||||||
@ -1527,6 +1548,8 @@
|
|||||||
137327E51AA5CF210034F82E /* RCTTabBarManager.h */,
|
137327E51AA5CF210034F82E /* RCTTabBarManager.h */,
|
||||||
137327E61AA5CF210034F82E /* RCTTabBarManager.m */,
|
137327E61AA5CF210034F82E /* RCTTabBarManager.m */,
|
||||||
E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */,
|
E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */,
|
||||||
|
3D5AC70F1E0056BC000F9153 /* RCTTVView.h */,
|
||||||
|
3D5AC7101E0056BC000F9153 /* RCTTVView.m */,
|
||||||
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
|
13E0674F1A70F44B002CDEE1 /* RCTView.h */,
|
||||||
13E067501A70F44B002CDEE1 /* RCTView.m */,
|
13E067501A70F44B002CDEE1 /* RCTView.m */,
|
||||||
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */,
|
13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */,
|
||||||
@ -1734,6 +1757,8 @@
|
|||||||
391E86A21C623EC800009732 /* RCTTouchEvent.m */,
|
391E86A21C623EC800009732 /* RCTTouchEvent.m */,
|
||||||
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
|
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
|
||||||
83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */,
|
83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */,
|
||||||
|
3D5AC71E1E005750000F9153 /* RCTTVRemoteHandler.h */,
|
||||||
|
3D5AC71F1E005750000F9153 /* RCTTVRemoteHandler.m */,
|
||||||
1345A83A1B265A0E00583190 /* RCTURLRequestDelegate.h */,
|
1345A83A1B265A0E00583190 /* RCTURLRequestDelegate.h */,
|
||||||
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
|
1345A83B1B265A0E00583190 /* RCTURLRequestHandler.h */,
|
||||||
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
|
83CBBA4F1A601E3B00E9B192 /* RCTUtils.h */,
|
||||||
@ -1781,6 +1806,7 @@
|
|||||||
3D302F361DF828F800D6DDAE /* RCTErrorInfo.h in Headers */,
|
3D302F361DF828F800D6DDAE /* RCTErrorInfo.h in Headers */,
|
||||||
3D302F371DF828F800D6DDAE /* RCTEventDispatcher.h in Headers */,
|
3D302F371DF828F800D6DDAE /* RCTEventDispatcher.h in Headers */,
|
||||||
3D302F381DF828F800D6DDAE /* RCTFrameUpdate.h in Headers */,
|
3D302F381DF828F800D6DDAE /* RCTFrameUpdate.h in Headers */,
|
||||||
|
3D5AC7221E005763000F9153 /* RCTTVRemoteHandler.h in Headers */,
|
||||||
3D302F391DF828F800D6DDAE /* RCTImageSource.h in Headers */,
|
3D302F391DF828F800D6DDAE /* RCTImageSource.h in Headers */,
|
||||||
3D302F3A1DF828F800D6DDAE /* RCTInvalidating.h in Headers */,
|
3D302F3A1DF828F800D6DDAE /* RCTInvalidating.h in Headers */,
|
||||||
3D302F3B1DF828F800D6DDAE /* RCTJavaScriptExecutor.h in Headers */,
|
3D302F3B1DF828F800D6DDAE /* RCTJavaScriptExecutor.h in Headers */,
|
||||||
@ -1854,6 +1880,7 @@
|
|||||||
3D302F841DF828F800D6DDAE /* RCTPointerEvents.h in Headers */,
|
3D302F841DF828F800D6DDAE /* RCTPointerEvents.h in Headers */,
|
||||||
3D302F851DF828F800D6DDAE /* RCTProgressViewManager.h in Headers */,
|
3D302F851DF828F800D6DDAE /* RCTProgressViewManager.h in Headers */,
|
||||||
3D302F861DF828F800D6DDAE /* RCTRefreshControl.h in Headers */,
|
3D302F861DF828F800D6DDAE /* RCTRefreshControl.h in Headers */,
|
||||||
|
3D5AC7131E0056C4000F9153 /* RCTTVView.h in Headers */,
|
||||||
3D302F871DF828F800D6DDAE /* RCTRefreshControlManager.h in Headers */,
|
3D302F871DF828F800D6DDAE /* RCTRefreshControlManager.h in Headers */,
|
||||||
A2440AA41DF8D865006E7BFC /* RCTReloadCommand.h in Headers */,
|
A2440AA41DF8D865006E7BFC /* RCTReloadCommand.h in Headers */,
|
||||||
3D302F881DF828F800D6DDAE /* RCTRootShadowView.h in Headers */,
|
3D302F881DF828F800D6DDAE /* RCTRootShadowView.h in Headers */,
|
||||||
@ -1865,6 +1892,7 @@
|
|||||||
3D302F8E1DF828F800D6DDAE /* RCTShadowView.h in Headers */,
|
3D302F8E1DF828F800D6DDAE /* RCTShadowView.h in Headers */,
|
||||||
3D302F8F1DF828F800D6DDAE /* RCTSlider.h in Headers */,
|
3D302F8F1DF828F800D6DDAE /* RCTSlider.h in Headers */,
|
||||||
3D302F901DF828F800D6DDAE /* RCTSliderManager.h in Headers */,
|
3D302F901DF828F800D6DDAE /* RCTSliderManager.h in Headers */,
|
||||||
|
3D5AC7191E0056E0000F9153 /* RCTTVNavigationEventEmitter.h in Headers */,
|
||||||
3D302F911DF828F800D6DDAE /* RCTSwitch.h in Headers */,
|
3D302F911DF828F800D6DDAE /* RCTSwitch.h in Headers */,
|
||||||
3D302F921DF828F800D6DDAE /* RCTSwitchManager.h in Headers */,
|
3D302F921DF828F800D6DDAE /* RCTSwitchManager.h in Headers */,
|
||||||
3D302F931DF828F800D6DDAE /* RCTTabBar.h in Headers */,
|
3D302F931DF828F800D6DDAE /* RCTTabBar.h in Headers */,
|
||||||
@ -2338,6 +2366,7 @@
|
|||||||
2D3B5EC91D9B095C00451313 /* RCTBorderDrawing.m in Sources */,
|
2D3B5EC91D9B095C00451313 /* RCTBorderDrawing.m in Sources */,
|
||||||
2D3B5ED31D9B097B00451313 /* RCTMapOverlay.m in Sources */,
|
2D3B5ED31D9B097B00451313 /* RCTMapOverlay.m in Sources */,
|
||||||
2D3B5E991D9B089A00451313 /* RCTDisplayLink.m in Sources */,
|
2D3B5E991D9B089A00451313 /* RCTDisplayLink.m in Sources */,
|
||||||
|
2D9F8B9B1DE398DB00A16144 /* RCTPlatform.m in Sources */,
|
||||||
2D3B5EBF1D9B093300451313 /* RCTJSCProfiler.m in Sources */,
|
2D3B5EBF1D9B093300451313 /* RCTJSCProfiler.m in Sources */,
|
||||||
2D3B5EA11D9B08B600451313 /* RCTModuleData.mm in Sources */,
|
2D3B5EA11D9B08B600451313 /* RCTModuleData.mm in Sources */,
|
||||||
2D3B5EEA1D9B09CD00451313 /* RCTTabBar.m in Sources */,
|
2D3B5EEA1D9B09CD00451313 /* RCTTabBar.m in Sources */,
|
||||||
@ -2393,15 +2422,18 @@
|
|||||||
2D3B5EC81D9B095800451313 /* RCTActivityIndicatorViewManager.m in Sources */,
|
2D3B5EC81D9B095800451313 /* RCTActivityIndicatorViewManager.m in Sources */,
|
||||||
3DCD185D1DF978E7007FE5A1 /* RCTReloadCommand.m in Sources */,
|
3DCD185D1DF978E7007FE5A1 /* RCTReloadCommand.m in Sources */,
|
||||||
2D3B5EC61D9B095000451313 /* RCTProfileTrampoline-x86_64.S in Sources */,
|
2D3B5EC61D9B095000451313 /* RCTProfileTrampoline-x86_64.S in Sources */,
|
||||||
|
3D5AC71A1E0056E0000F9153 /* RCTTVNavigationEventEmitter.m in Sources */,
|
||||||
3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */,
|
3D7A27E31DE325DA002E3F95 /* RCTJSCErrorHandling.mm in Sources */,
|
||||||
2D3B5ED01D9B097200451313 /* RCTMap.m in Sources */,
|
2D3B5ED01D9B097200451313 /* RCTMap.m in Sources */,
|
||||||
2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */,
|
2D3B5EA61D9B08CA00451313 /* RCTTouchEvent.m in Sources */,
|
||||||
2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */,
|
2D8C2E331DA40441000EE098 /* RCTMultipartStreamReader.m in Sources */,
|
||||||
2D3B5EF01D9B09E300451313 /* RCTWrapperViewController.m in Sources */,
|
2D3B5EF01D9B09E300451313 /* RCTWrapperViewController.m in Sources */,
|
||||||
|
3D5AC7141E0056C7000F9153 /* RCTTVView.m in Sources */,
|
||||||
2D3B5EEC1D9B09D400451313 /* RCTTabBarItemManager.m in Sources */,
|
2D3B5EEC1D9B09D400451313 /* RCTTabBarItemManager.m in Sources */,
|
||||||
2D3B5EB01D9B08FE00451313 /* RCTAlertManager.m in Sources */,
|
2D3B5EB01D9B08FE00451313 /* RCTAlertManager.m in Sources */,
|
||||||
2D3B5E9C1D9B08A300451313 /* RCTImageSource.m in Sources */,
|
2D3B5E9C1D9B08A300451313 /* RCTImageSource.m in Sources */,
|
||||||
3DDEC1521DDCE0CA0020BBDF /* JSCSamplingProfiler.m in Sources */,
|
3DDEC1521DDCE0CA0020BBDF /* JSCSamplingProfiler.m in Sources */,
|
||||||
|
3D5AC7231E005766000F9153 /* RCTTVRemoteHandler.m in Sources */,
|
||||||
2D3B5EC31D9B094800451313 /* RCTProfileTrampoline-arm.S in Sources */,
|
2D3B5EC31D9B094800451313 /* RCTProfileTrampoline-arm.S in Sources */,
|
||||||
2D3B5ED91D9B098E00451313 /* RCTNavItem.m in Sources */,
|
2D3B5ED91D9B098E00451313 /* RCTNavItem.m in Sources */,
|
||||||
2D74EAFA1DAE9590003B751B /* RCTMultipartDataTask.m in Sources */,
|
2D74EAFA1DAE9590003B751B /* RCTMultipartDataTask.m in Sources */,
|
||||||
|
33
React/Views/RCTTVView.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import <React/RCTView.h>
|
||||||
|
|
||||||
|
// A RCTView with additional properties and methods for user interaction using the Apple TV focus engine.
|
||||||
|
@interface RCTTVView : RCTView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV event handlers
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL isTVSelectable; // True if this view is TV-focusable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties for Apple TV focus parallax effects
|
||||||
|
*/
|
||||||
|
@property (nonatomic, copy) NSDictionary *tvParallaxProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TV preferred focus
|
||||||
|
*/
|
||||||
|
@property (nonatomic, assign) BOOL hasTVPreferredFocus;
|
||||||
|
|
||||||
|
@end
|
188
React/Views/RCTTVView.m
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTTVView.h"
|
||||||
|
|
||||||
|
#import "RCTAutoInsetsProtocol.h"
|
||||||
|
#import "RCTBorderDrawing.h"
|
||||||
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTConvert.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
#import "RCTRootViewInternal.h"
|
||||||
|
#import "RCTTVNavigationEventEmitter.h"
|
||||||
|
#import "RCTUtils.h"
|
||||||
|
#import "RCTView.h"
|
||||||
|
#import "UIView+React.h"
|
||||||
|
|
||||||
|
@implementation RCTTVView
|
||||||
|
{
|
||||||
|
UITapGestureRecognizer *_selectRecognizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame
|
||||||
|
{
|
||||||
|
if (self = [super initWithFrame:frame]) {
|
||||||
|
self.tvParallaxProperties = @{
|
||||||
|
@"enabled": @YES,
|
||||||
|
@"shiftDistanceX": @2.0f,
|
||||||
|
@"shiftDistanceY": @2.0f,
|
||||||
|
@"tiltAngle": @0.05f,
|
||||||
|
@"magnification": @1.0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)
|
||||||
|
|
||||||
|
- (void)setIsTVSelectable:(BOOL)isTVSelectable {
|
||||||
|
self->_isTVSelectable = isTVSelectable;
|
||||||
|
if(isTVSelectable) {
|
||||||
|
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSelect:)];
|
||||||
|
recognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
|
||||||
|
_selectRecognizer = recognizer;
|
||||||
|
[self addGestureRecognizer:_selectRecognizer];
|
||||||
|
} else {
|
||||||
|
if(_selectRecognizer) {
|
||||||
|
[self removeGestureRecognizer:_selectRecognizer];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)handleSelect:(UIGestureRecognizer *)r
|
||||||
|
{
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
|
||||||
|
object:@{@"eventType":@"select",@"tag":self.reactTag}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)isUserInteractionEnabled
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canBecomeFocused
|
||||||
|
{
|
||||||
|
return (self.isTVSelectable);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)addParallaxMotionEffects
|
||||||
|
{
|
||||||
|
// Size of shift movements
|
||||||
|
CGFloat const shiftDistanceX = [self.tvParallaxProperties[@"shiftDistanceX"] floatValue];
|
||||||
|
CGFloat const shiftDistanceY = [self.tvParallaxProperties[@"shiftDistanceY"] floatValue];
|
||||||
|
|
||||||
|
// Make horizontal movements shift the centre left and right
|
||||||
|
UIInterpolatingMotionEffect *xShift = [[UIInterpolatingMotionEffect alloc]
|
||||||
|
initWithKeyPath:@"center.x"
|
||||||
|
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
||||||
|
xShift.minimumRelativeValue = @( shiftDistanceX * -1.0f);
|
||||||
|
xShift.maximumRelativeValue = @( shiftDistanceX);
|
||||||
|
|
||||||
|
// Make vertical movements shift the centre up and down
|
||||||
|
UIInterpolatingMotionEffect *yShift = [[UIInterpolatingMotionEffect alloc]
|
||||||
|
initWithKeyPath:@"center.y"
|
||||||
|
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
|
||||||
|
yShift.minimumRelativeValue = @( shiftDistanceY * -1.0f);
|
||||||
|
yShift.maximumRelativeValue = @( shiftDistanceY);
|
||||||
|
|
||||||
|
// Size of tilt movements
|
||||||
|
CGFloat const tiltAngle = [self.tvParallaxProperties[@"tiltAngle"] floatValue];
|
||||||
|
|
||||||
|
// Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation.
|
||||||
|
UIInterpolatingMotionEffect *xTilt = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
|
||||||
|
|
||||||
|
// CATransform3D value for minimumRelativeValue
|
||||||
|
CATransform3D transMinimumTiltAboutY = CATransform3DIdentity;
|
||||||
|
transMinimumTiltAboutY.m34 = 1.0 / 500;
|
||||||
|
transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0);
|
||||||
|
|
||||||
|
// CATransform3D value for minimumRelativeValue
|
||||||
|
CATransform3D transMaximumTiltAboutY = CATransform3DIdentity;
|
||||||
|
transMaximumTiltAboutY.m34 = 1.0 / 500;
|
||||||
|
transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0);
|
||||||
|
|
||||||
|
// Set the transform property boundaries for the interpolation
|
||||||
|
xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutY];
|
||||||
|
xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutY];
|
||||||
|
|
||||||
|
// Now make vertical movements effect a rotation about the X axis for up and down rotation.
|
||||||
|
UIInterpolatingMotionEffect *yTilt = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"layer.transform" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
|
||||||
|
|
||||||
|
// CATransform3D value for minimumRelativeValue
|
||||||
|
CATransform3D transMinimumTiltAboutX = CATransform3DIdentity;
|
||||||
|
transMinimumTiltAboutX.m34 = 1.0 / 500;
|
||||||
|
transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0);
|
||||||
|
|
||||||
|
// CATransform3D value for minimumRelativeValue
|
||||||
|
CATransform3D transMaximumTiltAboutX = CATransform3DIdentity;
|
||||||
|
transMaximumTiltAboutX.m34 = 1.0 / 500;
|
||||||
|
transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0);
|
||||||
|
|
||||||
|
// Set the transform property boundaries for the interpolation
|
||||||
|
yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutX];
|
||||||
|
yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutX];
|
||||||
|
|
||||||
|
// Add all of the motion effects to this group
|
||||||
|
self.motionEffects = @[xShift, yShift, xTilt, yTilt];
|
||||||
|
|
||||||
|
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
|
||||||
|
|
||||||
|
[UIView animateWithDuration:0.2 animations:^{
|
||||||
|
self.transform = CGAffineTransformMakeScale(magnification, magnification);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
||||||
|
{
|
||||||
|
if (context.nextFocusedView == self && self.isTVSelectable ) {
|
||||||
|
[self becomeFirstResponder];
|
||||||
|
[coordinator addCoordinatedAnimations:^(void){
|
||||||
|
if([self.tvParallaxProperties[@"enabled"] boolValue]) {
|
||||||
|
[self addParallaxMotionEffects];
|
||||||
|
}
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
|
||||||
|
object:@{@"eventType":@"focus",@"tag":self.reactTag}];
|
||||||
|
} completion:^(void){}];
|
||||||
|
} else {
|
||||||
|
[coordinator addCoordinatedAnimations:^(void){
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
|
||||||
|
object:@{@"eventType":@"blur",@"tag":self.reactTag}];
|
||||||
|
[UIView animateWithDuration:0.2 animations:^{
|
||||||
|
self.transform = CGAffineTransformMakeScale(1, 1);
|
||||||
|
}];
|
||||||
|
|
||||||
|
for (UIMotionEffect *effect in [self.motionEffects copy]){
|
||||||
|
[self removeMotionEffect:effect];
|
||||||
|
}
|
||||||
|
} completion:^(void){}];
|
||||||
|
[self resignFirstResponder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus
|
||||||
|
{
|
||||||
|
_hasTVPreferredFocus = hasTVPreferredFocus;
|
||||||
|
if (hasTVPreferredFocus) {
|
||||||
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||||
|
UIView *rootview = self;
|
||||||
|
while(![rootview isReactRootView]) {
|
||||||
|
rootview = [rootview superview];
|
||||||
|
}
|
||||||
|
rootview = [rootview superview];
|
||||||
|
|
||||||
|
[(RCTRootView *)rootview setReactPreferredFocusedView:self];
|
||||||
|
[rootview setNeedsFocusUpdate];
|
||||||
|
[rootview updateFocusIfNeeded];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -183,4 +183,22 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
|
||||||
|
- (BOOL)isUserInteractionEnabled
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
|
||||||
|
{
|
||||||
|
if (context.nextFocusedView == self) {
|
||||||
|
[self becomeFirstResponder];
|
||||||
|
} else {
|
||||||
|
[self resignFirstResponder];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -29,6 +29,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectedIcon, UIImage)
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(systemIcon, UITabBarSystemItem)
|
RCT_EXPORT_VIEW_PROPERTY(systemIcon, UITabBarSystemItem)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
|
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(badgeColor, UIColor)
|
RCT_EXPORT_VIEW_PROPERTY(badgeColor, UIColor)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL)
|
||||||
RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem)
|
RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem)
|
||||||
{
|
{
|
||||||
view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title;
|
view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title;
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
#import "RCTView.h"
|
#import "RCTView.h"
|
||||||
#import "UIView+React.h"
|
#import "UIView+React.h"
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
#import "RCTTVView.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@implementation RCTConvert(UIAccessibilityTraits)
|
@implementation RCTConvert(UIAccessibilityTraits)
|
||||||
|
|
||||||
RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
|
RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
|
||||||
@ -57,7 +61,11 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
- (UIView *)view
|
- (UIView *)view
|
||||||
{
|
{
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
return [RCTTVView new];
|
||||||
|
#else
|
||||||
return [RCTView new];
|
return [RCTView new];
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTShadowView *)shadowView
|
- (RCTShadowView *)shadowView
|
||||||
@ -98,6 +106,13 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
#pragma mark - View properties
|
#pragma mark - View properties
|
||||||
|
|
||||||
|
#if TARGET_OS_TV
|
||||||
|
// Apple TV properties
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary)
|
||||||
|
#endif
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits)
|
RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
|
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
|
||||||
|
@ -11,7 +11,7 @@ XCODE_PROJECT="Examples/UIExplorer/UIExplorer.xcodeproj"
|
|||||||
XCODE_SCHEME="UIExplorer-tvOS"
|
XCODE_SCHEME="UIExplorer-tvOS"
|
||||||
XCODE_SDK="appletvsimulator"
|
XCODE_SDK="appletvsimulator"
|
||||||
if [ -z ${XCODE_DESTINATION+x} ]; then
|
if [ -z ${XCODE_DESTINATION+x} ]; then
|
||||||
XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p,OS=9.2"
|
XCODE_DESTINATION="platform=tvOS Simulator,name=Apple TV 1080p,OS=10.0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
. ./scripts/objc-test.sh
|
. ./scripts/objc-test.sh
|
||||||
|