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 all 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
16 changes: 11 additions & 5 deletions Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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 @@ -174,6 +175,9 @@ const 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 @@ -232,11 +236,13 @@ const TouchableHighlight = createReactClass({

touchableHandlePress: function(e: PressEvent) {
clearTimeout(this._hideTimeout);
this._showUnderlay();
this._hideTimeout = setTimeout(
this._hideUnderlay,
this.props.delayPressOut,
);
if (!Platform.isTVOS) {
this._showUnderlay();
this._hideTimeout = setTimeout(
this._hideUnderlay,
this.props.delayPressOut,
);
}
this.props.onPress && this.props.onPress(e);
},

Expand Down
3 changes: 3 additions & 0 deletions RNTester/js/ListExampleShared.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ 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 @@ -191,7 +191,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
93 changes: 76 additions & 17 deletions React/Views/RCTTVView.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,50 @@ @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 {
self->_isTVSelectable = isTVSelectable;
if(isTVSelectable) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSelect:)];
if (isTVSelectable) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleSelect:)];
recognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
_selectRecognizer = recognizer;
[self addGestureRecognizer:_selectRecognizer];
Expand All @@ -59,8 +85,37 @@ - (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 All @@ -81,23 +136,25 @@ - (void)addParallaxMotionEffects

// Make horizontal movements shift the centre left and right
UIInterpolatingMotionEffect *xShift = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
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];
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];
UIInterpolatingMotionEffect *xTilt = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"layer.transform"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];

// CATransform3D value for minimumRelativeValue
CATransform3D transMinimumTiltAboutY = CATransform3DIdentity;
Expand All @@ -114,7 +171,9 @@ - (void)addParallaxMotionEffects
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];
UIInterpolatingMotionEffect *yTilt = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"layer.transform"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];

// CATransform3D value for minimumRelativeValue
CATransform3D transMinimumTiltAboutX = CATransform3DIdentity;
Expand Down