Skip to content

Commit

Permalink
onPress animation with magnification
Browse files Browse the repository at this point in the history
Summary:
Related to: #15454

Motivation: Improve tvOS feeling for TouchableHighlight

![changewithaniamtion](https://user-images.githubusercontent.com/7658664/29193477-b99b4a10-7e25-11e7-8b31-e0e4ca9d7720.gif)

- When you select the button he is focus and the underlay is show
- When you press the button, there is an animation, but after the animation, the focus is on the button and the underlay is show

Play with tvParallaxProperties on tvOS, test with and without patch just to see the actual behaviour
```
			<TouchableHighlight
						tvParallaxProperties={{
							enabled: true,
							shiftDistanceX: 0,
							shiftDistanceY: 0,
							tiltAngle: 0,
							magnification: 1.1,
                                                        pressMagnification: 1.0,
							pressDuration: 0.3,
						}}
						underlayColor="black"
						onShowUnderlay={() => (console.log("onShowUnderlay")}
						onHideUnderlay={() =>  (console.log("onHideUnderlay")}
						onPress={() =>  (console.log("onPress")}
					>
						<Image
							style={styles.image}
							source={ uri: 'https://www.facebook.com/images/fb_icon_325x325.png' }
						/>
					</TouchableHighlight>
```
Closes #15455

Differential Revision: D6887437

Pulled By: hramos

fbshipit-source-id: e18b695068bc99643ba4006fb3f39215b38a74c1
  • Loading branch information
JulienKode authored and facebook-github-bot committed Feb 27, 2018
1 parent 1346bf8 commit 6c353fd
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 23 deletions.
3 changes: 3 additions & 0 deletions Libraries/Components/AppleTV/TVViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ var TVViewPropTypes = {
* 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
*/
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 @@ -13,6 +13,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 @@ -172,6 +173,9 @@ const TouchableHighlight = createReactClass({
* 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
*/
Expand Down Expand Up @@ -246,11 +250,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 @@ -62,6 +62,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 @@ -189,7 +189,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 @@ -55,6 +55,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 @@ -27,24 +27,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 @@ -57,8 +83,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 @@ -79,23 +134,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 @@ -112,7 +169,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

0 comments on commit 6c353fd

Please sign in to comment.