From 0bca26d806970680f19969d11c4ef5490671ea51 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Sun, 19 Nov 2017 06:01:15 -0500 Subject: [PATCH 01/11] Preview API --- example/src/components/Row.js | 35 ++++++++++++++++++++--------------- example/src/screens/Types.js | 21 ++++++++++++++++++++- ios/RCCNavigationController.m | 14 ++++++++++++++ ios/RCCViewController.h | 2 ++ ios/RCCViewController.m | 28 ++++++++++++++++++++++++++-- 5 files changed, 82 insertions(+), 18 deletions(-) diff --git a/example/src/components/Row.js b/example/src/components/Row.js index 4857870eff2..ed7dacdadab 100644 --- a/example/src/components/Row.js +++ b/example/src/components/Row.js @@ -2,27 +2,32 @@ import React from 'react'; import PropTypes from 'prop-types'; import {StyleSheet, View, Text, TouchableHighlight, Platform} from 'react-native'; -function Row({title, onPress, platform, testID}) { - if (platform && platform !== Platform.OS) { - return ; - } +class Row extends React.Component { + render() { + const {title, onPress, onPressIn, platform, testID} = this.props; + if (platform && platform !== Platform.OS) { + return ; + } - return ( - - - {title} - - - ); + return ( + + + {title} + + + ); + } } Row.propTypes = { title: PropTypes.string.isRequired, onPress: PropTypes.func.isRequired, + onPressIn: PropTypes.func }; const styles = StyleSheet.create({ diff --git a/example/src/screens/Types.js b/example/src/screens/Types.js index d0bc2a81144..3fb42c5be0f 100644 --- a/example/src/screens/Types.js +++ b/example/src/screens/Types.js @@ -1,5 +1,5 @@ import React from 'react'; -import {StyleSheet, View, Text, ScrollView, TouchableHighlight} from 'react-native'; +import {StyleSheet, View, Text, ScrollView, TouchableHighlight, findNodeHandle} from 'react-native'; import Row from '../components/Row'; class Types extends React.Component { @@ -18,6 +18,16 @@ class Types extends React.Component { }); }; + previewScreen = () => { + this.props.navigator.push({ + screen: 'example.Types.Push', + title: 'New Screen', + passProps: { + previewViewID: findNodeHandle(this.previewRef) + } + }); + } + pushTopTabsScreen = () => { this.props.navigator.push({ screen: 'example.Types.TopTabs', @@ -69,6 +79,15 @@ class Types extends React.Component { + { + this.previewRef = ref; + }} + title={'Preview Screen'} + testID={'previewScreen'} + onPress={this.pushScreen} + onPressIn={this.previewScreen} + /> diff --git a/ios/RCCNavigationController.m b/ios/RCCNavigationController.m index b1d435ecaaf..95a4e62ed3e 100755 --- a/ios/RCCNavigationController.m +++ b/ios/RCCNavigationController.m @@ -2,6 +2,7 @@ #import "RCCViewController.h" #import "RCCManager.h" #import +#import #import #import #import "RCCTitleViewHelper.h" @@ -143,6 +144,19 @@ - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actio [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO]; } + NSString *previewViewID = passProps[@"previewViewID"]; + if (previewViewID) { + RCCViewController *topViewController = ((RCCViewController*)self.topViewController); + dispatch_async(RCTGetUIManagerQueue(), ^{ + [bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + UIView *view = viewRegistry[passProps[@"previewViewID"]]; + [topViewController setPreviewView:view]; + }]; + }); + [topViewController setPreviewController:viewController]; + return; + } + NSString *animationType = actionParams[@"animationType"]; if ([animationType isEqualToString:@"fade"]) { diff --git a/ios/RCCViewController.h b/ios/RCCViewController.h index 761402572c2..eef59eac78b 100755 --- a/ios/RCCViewController.h +++ b/ios/RCCViewController.h @@ -16,6 +16,8 @@ extern NSString* const RCCViewControllerCancelReactTouchesNotification; - (void)setStyleOnInit; - (void)updateStyle; - (void)setNavBarVisibilityChange:(BOOL)animated; +- (void)setPreviewController:(RCCViewController *)controller; +- (void)setPreviewView:(UIView *)view; @end diff --git a/ios/RCCViewController.m b/ios/RCCViewController.m index 7fb9d83ce3f..0cdf8dd1076 100755 --- a/ios/RCCViewController.m +++ b/ios/RCCViewController.m @@ -19,7 +19,7 @@ const NSInteger BLUR_NAVBAR_TAG = 78264802; const NSInteger TRANSPARENT_NAVBAR_TAG = 78264803; -@interface RCCViewController() +@interface RCCViewController() @property (nonatomic) BOOL _hidesBottomBarWhenPushed; @property (nonatomic) BOOL _statusBarHideWithNavBar; @property (nonatomic) BOOL _statusBarHidden; @@ -29,7 +29,21 @@ @interface RCCViewController() @property (nonatomic, weak) id originalInteractivePopGestureDelegate; @end -@implementation RCCViewController +@implementation RCCViewController { + RCCViewController *_previewController; + UIView *_previewView; +} + +- (void)setPreviewController:(RCCViewController *)controller { + _previewController = controller; +} + +- (void)setPreviewView:(UIView *)view { + _previewView = view; + if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { + [self registerForPreviewingWithDelegate:(id)self sourceView:_previewView]; + } +} -(UIImageView *)navBarHairlineImageView { if (!_navBarHairlineImageView) { @@ -692,5 +706,15 @@ -(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return !disabledBackGestureBool; } +#pragma mark - UIViewControllerPreviewingDelegate +- (UIViewController *)previewingContext:(id)previewingContext viewControllerForLocation:(CGPoint)location { + return _previewController; +} + +- (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit { + [self sendScreenChangedEvent:@"didCommitPreview"]; + [self.navigationController pushViewController:_previewController animated:false]; +} + @end From 43729dc78424b387029b55dced6cf95accde7675 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Tue, 21 Nov 2017 20:38:24 -0500 Subject: [PATCH 02/11] Added preview actions and fail-guard wrappers --- example/src/screens/NavigationTypes.js | 18 +++++-- example/src/screens/types/Push.js | 18 ++++++- ios/RCCNavigationController.m | 32 ++++++++---- ios/RCCViewController.h | 3 ++ ios/RCCViewController.m | 72 +++++++++++++++++++------- 5 files changed, 109 insertions(+), 34 deletions(-) diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index 0127bb14db5..b4cd936f835 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -39,7 +39,19 @@ class NavigationTypes extends React.Component { screen: 'example.Types.Push', title: 'New Screen', passProps: { - previewViewID: findNodeHandle(this.previewRef) + previewViewID: findNodeHandle(this.previewRef), + previewActions: [{ + id: 'action-cancel', + title: 'Cancel' + }, { + id: 'action-delete', + title: 'Delete', + actions: [{ + id: 'action-delete-sure', + title: 'Are you sure?', + style: 'destructive' + }] + }] } }); }; @@ -118,9 +130,7 @@ class NavigationTypes extends React.Component { { - this.previewRef = ref; - }} + ref={(ref) => (this.previewRef = ref)} title={'Preview Screen'} testID={'previewScreen'} onPress={this.pushScreen} diff --git a/example/src/screens/types/Push.js b/example/src/screens/types/Push.js index e9c2c4cefc4..8a5bde6d2a8 100644 --- a/example/src/screens/types/Push.js +++ b/example/src/screens/types/Push.js @@ -1,8 +1,24 @@ import React, {Component} from 'react'; -import {StyleSheet, View, Text, Button} from 'react-native'; +import {StyleSheet, View, Text, Button, Alert} from 'react-native'; class Push extends Component { + constructor(props) { + super(props); + this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); + } + + onNavigatorEvent(event) { + if (event.type === 'PreviewActionPress') { + if (event.id === 'action-cancel') { + Alert.alert('Cancelled'); + } + if (event.id === 'action-delete-sure') { + Alert.alert('Deleted'); + } + } + } + onPushAnother = () => { this.props.navigator.push({ screen: 'example.Types.Push', diff --git a/ios/RCCNavigationController.m b/ios/RCCNavigationController.m index 5162c2e92e9..26a81bbd4fd 100755 --- a/ios/RCCNavigationController.m +++ b/ios/RCCNavigationController.m @@ -3,6 +3,9 @@ #import "RCCManager.h" #import #import +#if __has_include() +#import +#endif #import #import #import @@ -161,18 +164,27 @@ - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actio { [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO]; } - + + NSArray *previewActions = passProps[@"previewActions"]; NSString *previewViewID = passProps[@"previewViewID"]; if (previewViewID) { - RCCViewController *topViewController = ((RCCViewController*)self.topViewController); - dispatch_async(RCTGetUIManagerQueue(), ^{ - [bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - UIView *view = viewRegistry[passProps[@"previewViewID"]]; - [topViewController setPreviewView:view]; - }]; - }); - [topViewController setPreviewController:viewController]; - return; + if ([self.topViewController isKindOfClass:[RCCViewController class]]) + { + RCCViewController *topViewController = ((RCCViewController*)self.topViewController); + viewController.previewActions = previewActions; + if (topViewController.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) + { + dispatch_async(RCTGetUIManagerQueue(), ^{ + [bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { + UIView *view = viewRegistry[passProps[@"previewViewID"]]; + topViewController.previewView = view; + [topViewController registerForPreviewingWithDelegate:(id)topViewController sourceView:view]; + }]; + }); + topViewController.previewController = viewController; + } + return; + } } NSString *animationType = actionParams[@"animationType"]; diff --git a/ios/RCCViewController.h b/ios/RCCViewController.h index 8bdcca127dd..968fead5a5d 100755 --- a/ios/RCCViewController.h +++ b/ios/RCCViewController.h @@ -18,6 +18,9 @@ extern NSString* const RCCViewControllerCancelReactTouchesNotification; @property (nonatomic, strong) NSString *controllerId; @property (nonatomic, strong) NSString *commandType; @property (nonatomic, strong) NSString *timestamp; +@property (nonatomic) RCCViewController *previewController; +@property (nonatomic) UIView *previewView; +@property (nonatomic) NSArray *previewActions; + (UIViewController*)controllerWithLayout:(NSDictionary *)layout globalProps:(NSDictionary *)globalProps bridge:(RCTBridge *)bridge; diff --git a/ios/RCCViewController.m b/ios/RCCViewController.m index 7817aacf3a8..5b8d0de44bb 100755 --- a/ios/RCCViewController.m +++ b/ios/RCCViewController.m @@ -29,21 +29,7 @@ @interface RCCViewController() originalInteractivePopGestureDelegate; @end -@implementation RCCViewController { - RCCViewController *_previewController; - UIView *_previewView; -} - -- (void)setPreviewController:(RCCViewController *)controller { - _previewController = controller; -} - -- (void)setPreviewView:(UIView *)view { - _previewView = view; - if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { - [self registerForPreviewingWithDelegate:(id)self sourceView:_previewView]; - } -} +@implementation RCCViewController -(UIImageView *)navBarHairlineImageView { if (!_navBarHairlineImageView) { @@ -792,6 +778,37 @@ -(void)addExternalVCIfNecessary:(NSDictionary*)props } } +#pragma mark - Preview Actions + +- (void)onActionPress:(NSString *)id { + if ([self.view isKindOfClass:[RCTRootView class]]) { + RCTRootView *rootView = (RCTRootView *)self.view; + if (rootView.appProperties && rootView.appProperties[@"navigatorEventID"]) { + [[[RCCManager sharedInstance] getBridge].eventDispatcher + sendAppEventWithName:rootView.appProperties[@"navigatorEventID"] + body:@{ + @"type": @"PreviewActionPress", + @"id": id + }]; + } + } +} + +- (UIPreviewAction *) convertAction:(NSDictionary *)action { + NSString *actionId = action[@"id"]; + NSString *actionTitle = action[@"title"]; + UIPreviewActionStyle actionStyle = UIPreviewActionStyleDefault; + if ([action[@"style"] isEqualToString:@"selected"]) { + actionStyle = UIPreviewActionStyleSelected; + } + if ([action[@"style"] isEqualToString:@"destructive"]) { + actionStyle = UIPreviewActionStyleDestructive; + } + return [UIPreviewAction actionWithTitle:actionTitle style:actionStyle handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) { + [self onActionPress:actionId]; + }]; +} + #pragma mark - NewRelic - (NSString*) customNewRelicInteractionName @@ -830,13 +847,30 @@ -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogniz #pragma mark - UIViewControllerPreviewingDelegate - (UIViewController *)previewingContext:(id)previewingContext viewControllerForLocation:(CGPoint)location { - return _previewController; + return self.previewController; } - (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit { - [self sendScreenChangedEvent:@"didCommitPreview"]; - [self.navigationController pushViewController:_previewController animated:false]; + [self.navigationController pushViewController:self.previewController animated:false]; +} + +- (NSArray> *)previewActionItems { + NSMutableArray *actions = [[NSMutableArray alloc] init]; + for (NSDictionary *previewAction in self.previewActions) { + UIPreviewAction *action = [self convertAction:previewAction]; + NSDictionary *actionActions = previewAction[@"actions"]; + if (actionActions.count > 0) { + NSMutableArray *group = [[NSMutableArray alloc] init]; + for (NSDictionary *previewGroupAction in actionActions) { + [group addObject:[self convertAction:previewGroupAction]]; + } + UIPreviewActionGroup *actionGroup = [UIPreviewActionGroup actionGroupWithTitle:action.title style:UIPreviewActionStyleDefault actions:group]; + [actions addObject:actionGroup]; + } else { + [actions addObject:action]; + } + } + return actions; } - @end From 11a90e72f68d851ffdce208a20a5b438e7572799 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Tue, 21 Nov 2017 20:49:15 -0500 Subject: [PATCH 03/11] Remove old setters --- ios/RCCViewController.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/RCCViewController.h b/ios/RCCViewController.h index 968fead5a5d..23397477458 100755 --- a/ios/RCCViewController.h +++ b/ios/RCCViewController.h @@ -30,8 +30,6 @@ extern NSString* const RCCViewControllerCancelReactTouchesNotification; - (void)setStyleOnInit; - (void)updateStyle; - (void)setNavBarVisibilityChange:(BOOL)animated; -- (void)setPreviewController:(RCCViewController *)controller; -- (void)setPreviewView:(UIView *)view; @end From 62f6b38bff7bc8b15a5b7b69610bf5264c86880e Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Tue, 21 Nov 2017 21:00:31 -0500 Subject: [PATCH 04/11] Moved from passProps to actionParams --- example/src/screens/NavigationTypes.js | 26 +++++++++---------- ios/RCCNavigationController.m | 6 ++--- .../platformSpecificDeprecated.ios.js | 2 ++ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index b4cd936f835..dada88c3872 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -38,21 +38,19 @@ class NavigationTypes extends React.Component { this.props.navigator.push({ screen: 'example.Types.Push', title: 'New Screen', - passProps: { - previewViewID: findNodeHandle(this.previewRef), - previewActions: [{ - id: 'action-cancel', - title: 'Cancel' - }, { - id: 'action-delete', - title: 'Delete', - actions: [{ - id: 'action-delete-sure', - title: 'Are you sure?', - style: 'destructive' - }] + previewViewID: findNodeHandle(this.previewRef), + previewActions: [{ + id: 'action-cancel', + title: 'Cancel' + }, { + id: 'action-delete', + title: 'Delete', + actions: [{ + id: 'action-delete-sure', + title: 'Are you sure?', + style: 'destructive' }] - } + }] }); }; diff --git a/ios/RCCNavigationController.m b/ios/RCCNavigationController.m index 26a81bbd4fd..0465a6bd12a 100755 --- a/ios/RCCNavigationController.m +++ b/ios/RCCNavigationController.m @@ -165,8 +165,8 @@ - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actio [self setButtons:rightButtons viewController:viewController side:@"right" animated:NO]; } - NSArray *previewActions = passProps[@"previewActions"]; - NSString *previewViewID = passProps[@"previewViewID"]; + NSArray *previewActions = actionParams[@"previewActions"]; + NSString *previewViewID = actionParams[@"previewViewID"]; if (previewViewID) { if ([self.topViewController isKindOfClass:[RCCViewController class]]) { @@ -176,7 +176,7 @@ - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actio { dispatch_async(RCTGetUIManagerQueue(), ^{ [bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - UIView *view = viewRegistry[passProps[@"previewViewID"]]; + UIView *view = viewRegistry[previewViewID]; topViewController.previewView = view; [topViewController registerForPreviewingWithDelegate:(id)topViewController sourceView:view]; }]; diff --git a/src/deprecated/platformSpecificDeprecated.ios.js b/src/deprecated/platformSpecificDeprecated.ios.js index ff95dfb0717..8a316d59947 100644 --- a/src/deprecated/platformSpecificDeprecated.ios.js +++ b/src/deprecated/platformSpecificDeprecated.ios.js @@ -267,6 +267,8 @@ function navigatorPush(navigator, params) { backButtonHidden: params.backButtonHidden, leftButtons: navigatorButtons.leftButtons, rightButtons: navigatorButtons.rightButtons, + previewViewID: params.previewViewID, + previewActions: params.previewActions, timestamp: Date.now() }); } From 8fbb7dd65cfb7e555de75a0e3a1b102ec274bcc5 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Tue, 21 Nov 2017 21:35:30 -0500 Subject: [PATCH 05/11] Move findNodeHandle to internal method --- example/src/components/Row.js | 2 +- example/src/screens/NavigationTypes.js | 2 +- src/deprecated/platformSpecificDeprecated.ios.js | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/example/src/components/Row.js b/example/src/components/Row.js index ed7dacdadab..97e484b9da9 100644 --- a/example/src/components/Row.js +++ b/example/src/components/Row.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {StyleSheet, View, Text, TouchableHighlight, Platform} from 'react-native'; -class Row extends React.Component { +class Row extends React.PureComponent { render() { const {title, onPress, onPressIn, platform, testID} = this.props; if (platform && platform !== Platform.OS) { diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index dada88c3872..d7ab0d6a3a2 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -38,7 +38,7 @@ class NavigationTypes extends React.Component { this.props.navigator.push({ screen: 'example.Types.Push', title: 'New Screen', - previewViewID: findNodeHandle(this.previewRef), + previewView: this.previewRef, previewActions: [{ id: 'action-cancel', title: 'Cancel' diff --git a/src/deprecated/platformSpecificDeprecated.ios.js b/src/deprecated/platformSpecificDeprecated.ios.js index 8a316d59947..b2af377c37b 100644 --- a/src/deprecated/platformSpecificDeprecated.ios.js +++ b/src/deprecated/platformSpecificDeprecated.ios.js @@ -1,4 +1,6 @@ /*eslint-disable*/ +import { Component } from 'react'; +import { findNodeHandle } from 'react-native'; import Navigation from './../Navigation'; import Controllers, {Modal, Notification, ScreenUtils} from './controllers'; const React = Controllers.hijackReact(); @@ -232,7 +234,11 @@ function navigatorPush(navigator, params) { console.error('Navigator.push(params): params.screen is required'); return; } + let previewViewID; const screenInstanceID = _.uniqueId('screenInstanceID'); + if (params.previewView instanceof Component) { + previewViewID = findNodeHandle(params.previewView) + } const { navigatorStyle, navigatorButtons, @@ -267,7 +273,7 @@ function navigatorPush(navigator, params) { backButtonHidden: params.backButtonHidden, leftButtons: navigatorButtons.leftButtons, rightButtons: navigatorButtons.rightButtons, - previewViewID: params.previewViewID, + previewViewID: previewViewID, previewActions: params.previewActions, timestamp: Date.now() }); From 8fde44ce14a6874462cd8a97a90e83071ce395fb Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Tue, 21 Nov 2017 21:36:18 -0500 Subject: [PATCH 06/11] Removing findNodeHandle --- example/src/screens/NavigationTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index d7ab0d6a3a2..dbcf348a20d 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -1,5 +1,5 @@ import React from 'react'; -import {StyleSheet, ScrollView, findNodeHandle} from 'react-native'; +import {StyleSheet, ScrollView} from 'react-native'; import Row from '../components/Row'; class NavigationTypes extends React.Component { From 5c0c1dccf680d0ab6f9d9d3c5624e096c3c578fb Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Wed, 22 Nov 2017 10:08:07 -0500 Subject: [PATCH 07/11] Added height and option to dont 'pop' the view controller --- docs/screen-api.md | 11 ++++++++++- example/src/screens/NavigationTypes.js | 2 ++ ios/RCCNavigationController.m | 5 +++++ ios/RCCViewController.h | 1 + ios/RCCViewController.m | 6 +++++- src/deprecated/platformSpecificDeprecated.ios.js | 8 ++++++++ 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/docs/screen-api.md b/docs/screen-api.md index c015df0e746..ca648aecb96 100644 --- a/docs/screen-api.md +++ b/docs/screen-api.md @@ -17,7 +17,16 @@ this.props.navigator.push({ backButtonTitle: undefined, // override the back button title (optional) backButtonHidden: false, // hide the back button altogether (optional) navigatorStyle: {}, // override the navigator style for the pushed screen (optional) - navigatorButtons: {} // override the nav buttons for the pushed screen (optional) + navigatorButtons: {}, // override the nav buttons for the pushed screen (optional) + previewView: undefined, // react ref or node id (optional) + previewHeight: undefined, // set preview height, defaults to full height (optional) + previewCommit: true, // weither to commit to push preview controller to the navigation stack (optional) + previewActions: [{ + id: '', // action id (required) + title: '', // action title (required) + style: undefined, // 'selected' or 'destructive' (optional) + actions: [], // List of sub-actions + }], }); ``` diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index dbcf348a20d..8387941269a 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -38,6 +38,8 @@ class NavigationTypes extends React.Component { this.props.navigator.push({ screen: 'example.Types.Push', title: 'New Screen', + previewCommit: false, + previewHeight: 250, previewView: this.previewRef, previewActions: [{ id: 'action-cancel', diff --git a/ios/RCCNavigationController.m b/ios/RCCNavigationController.m index 0465a6bd12a..4bdbffa0664 100755 --- a/ios/RCCNavigationController.m +++ b/ios/RCCNavigationController.m @@ -172,6 +172,11 @@ - (void)performAction:(NSString*)performAction actionParams:(NSDictionary*)actio { RCCViewController *topViewController = ((RCCViewController*)self.topViewController); viewController.previewActions = previewActions; + viewController.previewCommit = actionParams[@"previewCommit"] ? [actionParams[@"previewCommit"] boolValue] : YES; + NSNumber *previewHeight = actionParams[@"previewHeight"]; + if (previewHeight) { + viewController.preferredContentSize = CGSizeMake(viewController.view.frame.size.width, [previewHeight floatValue]); + } if (topViewController.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) { dispatch_async(RCTGetUIManagerQueue(), ^{ diff --git a/ios/RCCViewController.h b/ios/RCCViewController.h index 23397477458..27ca96d415a 100755 --- a/ios/RCCViewController.h +++ b/ios/RCCViewController.h @@ -21,6 +21,7 @@ extern NSString* const RCCViewControllerCancelReactTouchesNotification; @property (nonatomic) RCCViewController *previewController; @property (nonatomic) UIView *previewView; @property (nonatomic) NSArray *previewActions; +@property (nonatomic) BOOL previewCommit; + (UIViewController*)controllerWithLayout:(NSDictionary *)layout globalProps:(NSDictionary *)globalProps bridge:(RCTBridge *)bridge; diff --git a/ios/RCCViewController.m b/ios/RCCViewController.m index 5b8d0de44bb..ffd9a785c48 100755 --- a/ios/RCCViewController.m +++ b/ios/RCCViewController.m @@ -851,7 +851,11 @@ - (UIViewController *)previewingContext:(id)previewi } - (void)previewingContext:(id)previewingContext commitViewController:(UIViewController *)viewControllerToCommit { - [self.navigationController pushViewController:self.previewController animated:false]; + if (self.previewController.previewCommit == YES) { + [self.previewController sendGlobalScreenEvent:@"willCommitPreview" endTimestampString:[self.previewController getTimestampString] shouldReset:YES]; + [self.previewController sendScreenChangedEvent:@"willCommitPreview"]; + [self.navigationController pushViewController:self.previewController animated:false]; + } } - (NSArray> *)previewActionItems { diff --git a/src/deprecated/platformSpecificDeprecated.ios.js b/src/deprecated/platformSpecificDeprecated.ios.js index b2af377c37b..7893cedd446 100644 --- a/src/deprecated/platformSpecificDeprecated.ios.js +++ b/src/deprecated/platformSpecificDeprecated.ios.js @@ -238,6 +238,10 @@ function navigatorPush(navigator, params) { const screenInstanceID = _.uniqueId('screenInstanceID'); if (params.previewView instanceof Component) { previewViewID = findNodeHandle(params.previewView) + } else if (typeof params.previewView === 'number') { + previewViewID = params.previewView; + } else { + console.error('Navigator.push(params): params.previewView is not a valid react view'); } const { navigatorStyle, @@ -249,6 +253,8 @@ function navigatorPush(navigator, params) { passProps.navigatorID = navigator.navigatorID; passProps.screenInstanceID = screenInstanceID; passProps.navigatorEventID = navigatorEventID; + passProps.previewViewID = previewViewID; + passProps.isPreview = !!previewViewID; params.navigationParams = { screenInstanceID, @@ -275,6 +281,8 @@ function navigatorPush(navigator, params) { rightButtons: navigatorButtons.rightButtons, previewViewID: previewViewID, previewActions: params.previewActions, + previewHeight: params.previewHeight, + previewCommit: params.previewCommit, timestamp: Date.now() }); } From c070f4d5ba75abc048b64b44fbf76e4ff253fbff Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Wed, 22 Nov 2017 10:19:50 -0500 Subject: [PATCH 08/11] Documentation update for peek and pop --- docs/screen-api.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/screen-api.md b/docs/screen-api.md index ca648aecb96..5a9d234ed73 100644 --- a/docs/screen-api.md +++ b/docs/screen-api.md @@ -18,14 +18,15 @@ this.props.navigator.push({ backButtonHidden: false, // hide the back button altogether (optional) navigatorStyle: {}, // override the navigator style for the pushed screen (optional) navigatorButtons: {}, // override the nav buttons for the pushed screen (optional) + // enable peek and pop - commited screen will have `isPreview` prop set as true. previewView: undefined, // react ref or node id (optional) previewHeight: undefined, // set preview height, defaults to full height (optional) - previewCommit: true, // weither to commit to push preview controller to the navigation stack (optional) + previewCommit: true, // commit to push preview controller to the navigation stack (optional) previewActions: [{ id: '', // action id (required) title: '', // action title (required) style: undefined, // 'selected' or 'destructive' (optional) - actions: [], // List of sub-actions + actions: [], // list of sub-actions }], }); ``` @@ -304,6 +305,8 @@ export default class ExampleScreen extends Component { break; case 'didDisappear': break; + case 'willCommitPreview': + break; } } } From 688cc402320f031c3b21a279a9d85b91c2b13a22 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Wed, 22 Nov 2017 10:22:39 -0500 Subject: [PATCH 09/11] Document how to access button press events --- docs/screen-api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/screen-api.md b/docs/screen-api.md index 5a9d234ed73..f4e7ac80f7b 100644 --- a/docs/screen-api.md +++ b/docs/screen-api.md @@ -22,7 +22,7 @@ this.props.navigator.push({ previewView: undefined, // react ref or node id (optional) previewHeight: undefined, // set preview height, defaults to full height (optional) previewCommit: true, // commit to push preview controller to the navigation stack (optional) - previewActions: [{ + previewActions: [{ // action presses can be detected with the `PreviewActionPress` event on the commited screen. id: '', // action id (required) title: '', // action title (required) style: undefined, // 'selected' or 'destructive' (optional) From 7bea2d8d045d44477493b198cd757d9bf84e5542 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Wed, 22 Nov 2017 10:24:04 -0500 Subject: [PATCH 10/11] Commit by default in example. Because it's cool --- example/src/screens/NavigationTypes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/src/screens/NavigationTypes.js b/example/src/screens/NavigationTypes.js index 8387941269a..4973829f74f 100644 --- a/example/src/screens/NavigationTypes.js +++ b/example/src/screens/NavigationTypes.js @@ -38,7 +38,7 @@ class NavigationTypes extends React.Component { this.props.navigator.push({ screen: 'example.Types.Push', title: 'New Screen', - previewCommit: false, + previewCommit: true, previewHeight: 250, previewView: this.previewRef, previewActions: [{ From acc9ea12b5dbe42b039285468a31a6ee108a2ca2 Mon Sep 17 00:00:00 2001 From: Birkir Gudjonsson Date: Thu, 23 Nov 2017 10:32:08 -0500 Subject: [PATCH 11/11] Docs and if elses ifs then --- docs/screen-api.md | 9 +++++++++ src/deprecated/platformSpecificDeprecated.ios.js | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/screen-api.md b/docs/screen-api.md index f4e7ac80f7b..52871761e7e 100644 --- a/docs/screen-api.md +++ b/docs/screen-api.md @@ -360,3 +360,12 @@ export default class ExampleScreen extends Component { } } ``` + +# Peek and pop (3D touch) + +react-native-navigation supports the [Peek and pop]( +https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/#//apple_ref/doc/uid/TP40016543-CH1-SW3) feature by setting a react view reference as a `previewView` parameter when doing a push, more options are available in the `push` section. + +You can define actions and listen for interactions on the pushed screen with the `PreviewActionPress` event. + +Previewed screens will have the prop `isPreview` that can be used to render different things when the screen is in the "Peek" state and will then recieve a navigator event of `willCommitPreview` when in the "Pop" state. \ No newline at end of file diff --git a/src/deprecated/platformSpecificDeprecated.ios.js b/src/deprecated/platformSpecificDeprecated.ios.js index cc75a4c6faf..6531ae52518 100644 --- a/src/deprecated/platformSpecificDeprecated.ios.js +++ b/src/deprecated/platformSpecificDeprecated.ios.js @@ -242,7 +242,7 @@ function navigatorPush(navigator, params) { previewViewID = findNodeHandle(params.previewView) } else if (typeof params.previewView === 'number') { previewViewID = params.previewView; - } else { + } else if (params.previewView) { console.error('Navigator.push(params): params.previewView is not a valid react view'); } const {