Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tvOS] onPress animation with magnification #15455

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Libraries/Components/AppleTV/TVViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ var TVViewPropTypes = {
* shiftDistanceY: Defaults to 2.0.
* tiltAngle: Defaults to 0.05.
* magnification: Defaults to 1.0.
* pressMagnification: Defaults to 1.0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's document these in https://github.com/facebook/react-native-website as well, since those are the docs that are displayed on the website itself.

Copy link
Contributor Author

@JulienKode JulienKode Feb 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the information @hramos I've created a pull request here facebook/react-native-website#176

* pressDuration: Defaults to 0.3.
* pressDelay: Defaults to 0.0.
*
* @platform ios
*/
Expand Down
17 changes: 11 additions & 6 deletions Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
const ColorPropType = require('ColorPropType');
const NativeMethodsMixin = require('NativeMethodsMixin');
const PropTypes = require('prop-types');
const Platform = require('Platform');
const React = require('React');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const StyleSheet = require('StyleSheet');
Expand Down Expand Up @@ -110,6 +111,9 @@ var TouchableHighlight = createReactClass({
* shiftDistanceY: Defaults to 2.0.
* tiltAngle: Defaults to 0.05.
* magnification: Defaults to 1.0.
* pressMagnification: Defaults to 1.0.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. These will need to be copied over to the react-native-website repo as well.

* pressDuration: Defaults to 0.3.
* pressDelay: Defaults to 0.0.
*
* @platform ios
*/
Expand Down Expand Up @@ -208,12 +212,13 @@ var TouchableHighlight = createReactClass({

touchableHandlePress: function(e: Event) {
this.clearTimeout(this._hideTimeout);
this._showUnderlay();
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment
* suppresses an error when upgrading Flow's support for React. To see the
* error delete this comment and run Flow. */
this._hideTimeout = this.setTimeout(this._hideUnderlay,
this.props.delayPressOut || 100);
if (!Platform.isTVOS) {
this._showUnderlay();
/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This comment
* suppresses an error when upgrading Flow's support for React. To see the
* error delete this comment and run Flow. */
this._hideTimeout = this.setTimeout(this._hideUnderlay, this.props.delayPressOut || 100);
}
this.props.onPress && this.props.onPress(e);
},

Expand Down
13 changes: 12 additions & 1 deletion Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,18 @@ var TouchableOpacity = createReactClass({
*/
activeOpacity: PropTypes.number,
/**
* Apple TV parallax effects
* *(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.
* pressMagnification: Defaults to 1.0.
* pressDuration: Defaults to 0.3.
* pressDelay: Defaults to 0.0.
*
* @platform ios
*/
tvParallaxProperties: PropTypes.object,
},
Expand Down
4 changes: 4 additions & 0 deletions RNTester/js/ListExampleShared.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class ItemComponent extends React.PureComponent<{
onPress={this._onPress}
onShowUnderlay={this.props.onShowUnderlay}
onHideUnderlay={this.props.onHideUnderlay}
tvParallaxProperties={{
pressMagnification: 1.1,
}}

style={horizontal ? styles.horizItem : styles.item}>
<View style={[
styles.row, horizontal && {width: HORIZ_WIDTH}, fixedHeight && {height: ITEM_HEIGHT}]}>
Expand Down
2 changes: 1 addition & 1 deletion RNTester/js/RNTesterList.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ const ComponentExamples: Array<RNTesterExample> = [
{
key: 'TouchableExample',
module: require('./TouchableExample'),
supportsTVOS: false,
supportsTVOS: true,
},
{
key: 'TransparentHitTestExample',
Expand Down
1 change: 1 addition & 0 deletions RNTester/js/TouchableExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ exports.examples = [
style={styles.wrapper}
activeOpacity={1}
animationVelocity={0}
tvParallaxProperties={{pressMagnification: 1.3, pressDuration: 0.6}}
underlayColor="rgb(210, 230, 255)"
onPress={() => console.log('custom THW text - highlight')}>
<View style={styles.wrapperCustom}>
Expand Down
75 changes: 66 additions & 9 deletions React/Views/RCTTVView.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,43 @@ @implementation RCTTVView
- (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
};
dispatch_once(&onceToken, ^{
defaultTVParallaxProperties = @{
@"enabled": @YES,
@"shiftDistanceX": @2.0f,
@"shiftDistanceY": @2.0f,
@"tiltAngle": @0.05f,
@"magnification": @1.0f,
@"pressMagnification": @1.0f,
@"pressDuration": @0.3f,
@"pressDelay": @0.0f
};
});
self.tvParallaxProperties = defaultTVParallaxProperties;
}

return self;
}

static NSDictionary* defaultTVParallaxProperties = nil;

static dispatch_once_t onceToken;


- (void)setTvParallaxProperties:(NSDictionary *)tvParallaxProperties {
if (_tvParallaxProperties == nil) {
_tvParallaxProperties = [defaultTVParallaxProperties copy];
return;
}
NSMutableDictionary *newParallaxProperties = [NSMutableDictionary dictionaryWithDictionary:_tvParallaxProperties];
for (NSString *k in [defaultTVParallaxProperties allKeys]) {
if (tvParallaxProperties[k]) {
newParallaxProperties[k] = tvParallaxProperties[k];
}
}
_tvParallaxProperties = [newParallaxProperties copy];
}

RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)

- (void)setIsTVSelectable:(BOOL)isTVSelectable {
Expand All @@ -59,8 +84,40 @@ - (void)setIsTVSelectable:(BOOL)isTVSelectable {

- (void)handleSelect:(__unused UIGestureRecognizer *)r
{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"select",@"tag":self.reactTag}];

if([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue];

// Duration of press animation
float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];

// Delay of press animation
float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue];

[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];

[UIView animateWithDuration:(pressDuration/2)
animations:^{
self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
}
completion:^(__unused BOOL finished1){
[UIView animateWithDuration:(pressDuration/2)
animations:^{
self.transform = CGAffineTransformMakeScale(magnification, magnification);
}
completion:^(__unused BOOL finished2) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"select",@"tag":self.reactTag}];
}];
}];

} else {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"select",@"tag":self.reactTag}];
}


}

- (BOOL)isUserInteractionEnabled
Expand Down