From ac3979594864434eb99cfc70b9c09071f106b94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ullrich=20Sch=C3=A4fer?= Date: Fri, 4 Jan 2019 13:06:03 -0800 Subject: [PATCH] Fixing ActionSheetIOS position after rotation on tablet (#22738) Summary: There's been a bug on iOS and iPad that the position of an action sheet using UIActionController isn't updated if the position of its anchor view changes due to rotating the device. A common scenario would be, presenting an action sheet from a right bar button item. Rotating the device will most likely change the bar button's X coordinate. The action sheets arrow would still point to the old position due to how it has been implemented so far. I used also reduced some code duplication between `-showActionSheetWithOptions` and `-showShareActionSheetWithOptions:` while at it. Changelog: ---------- [iOS] [Fixed] - Action Sheet position after rotation on tablet Pull Request resolved: https://github.com/facebook/react-native/pull/22738 Differential Revision: D13582810 Pulled By: PeteTheHeat fbshipit-source-id: a93065284b02efc41ae7378465521330a828a126 --- .../ActionSheetIOS/RCTActionSheetManager.m | 45 ++---- RNTester/js/ActionSheetIOSExample.js | 131 +++++++++++++++++- 2 files changed, 144 insertions(+), 32 deletions(-) diff --git a/Libraries/ActionSheetIOS/RCTActionSheetManager.m b/Libraries/ActionSheetIOS/RCTActionSheetManager.m index af1e202c962409..722b27b4897c81 100644 --- a/Libraries/ActionSheetIOS/RCTActionSheetManager.m +++ b/Libraries/ActionSheetIOS/RCTActionSheetManager.m @@ -32,20 +32,21 @@ - (dispatch_queue_t)methodQueue return dispatch_get_main_queue(); } -/* - * The `anchor` option takes a view to set as the anchor for the share - * popup to point to, on iPads running iOS 8. If it is not passed, it - * defaults to centering the share popup on screen without any arrows. - */ -- (CGRect)sourceRectInView:(UIView *)sourceView - anchorViewTag:(NSNumber *)anchorViewTag +- (void)presentViewController:(UIViewController *)alertController + onParentViewController:(UIViewController *)parentViewController + anchorViewTag:(NSNumber *)anchorViewTag { + alertController.modalPresentationStyle = UIModalPresentationPopover; + UIView *sourceView = parentViewController.view; + if (anchorViewTag) { - UIView *anchorView = [self.bridge.uiManager viewForReactTag:anchorViewTag]; - return [anchorView convertRect:anchorView.bounds toView:sourceView]; + sourceView = [self.bridge.uiManager viewForReactTag:anchorViewTag]; } else { - return (CGRect){sourceView.center, {1, 1}}; + alertController.popoverPresentationController.permittedArrowDirections = 0; } + alertController.popoverPresentationController.sourceView = sourceView; + alertController.popoverPresentationController.sourceRect = sourceView.bounds; + [parentViewController presentViewController:alertController animated:YES completion:nil]; } RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options @@ -79,9 +80,7 @@ - (CGRect)sourceRectInView:(UIView *)sourceView * defaults to centering the share popup on screen without any arrows. */ NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; - UIView *sourceView = controller.view; - CGRect sourceRect = [self sourceRectInView:sourceView anchorViewTag:anchorViewTag]; - + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message @@ -106,15 +105,8 @@ - (CGRect)sourceRectInView:(UIView *)sourceView index++; } - alertController.modalPresentationStyle = UIModalPresentationPopover; - alertController.popoverPresentationController.sourceView = sourceView; - alertController.popoverPresentationController.sourceRect = sourceRect; - if (!anchorViewTag) { - alertController.popoverPresentationController.permittedArrowDirections = 0; - } - [controller presentViewController:alertController animated:YES completion:nil]; - alertController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; + [self presentViewController:alertController onParentViewController:controller anchorViewTag:anchorViewTag]; } RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options @@ -173,17 +165,10 @@ - (CGRect)sourceRectInView:(UIView *)sourceView } }; - shareController.modalPresentationStyle = UIModalPresentationPopover; NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]]; - if (!anchorViewTag) { - shareController.popoverPresentationController.permittedArrowDirections = 0; - } - shareController.popoverPresentationController.sourceView = controller.view; - shareController.popoverPresentationController.sourceRect = [self sourceRectInView:controller.view anchorViewTag:anchorViewTag]; - - [controller presentViewController:shareController animated:YES completion:nil]; - shareController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]]; + + [self presentViewController:shareController onParentViewController:controller anchorViewTag:anchorViewTag]; } @end diff --git a/RNTester/js/ActionSheetIOSExample.js b/RNTester/js/ActionSheetIOSExample.js index 40dfc096b91474..59f4d1b8b67013 100644 --- a/RNTester/js/ActionSheetIOSExample.js +++ b/RNTester/js/ActionSheetIOSExample.js @@ -57,7 +57,10 @@ class ActionSheetExample extends React.Component { }; } -class ActionSheetTintExample extends React.Component<{}, $FlowFixMeState> { +class ActionSheetTintExample extends React.Component< + $FlowFixMeProps, + $FlowFixMeState, +> { state = { clicked: 'none', }; @@ -88,6 +91,52 @@ class ActionSheetTintExample extends React.Component<{}, $FlowFixMeState> { }; } +class ActionSheetAnchorExample extends React.Component< + $FlowFixMeProps, + $FlowFixMeState, +> { + state = { + clicked: 'none', + }; + + anchorRef = React.createRef(); + + render() { + return ( + + + + Click there to show the ActionSheet -> + + + HERE + + + Clicked button: {this.state.clicked} + + ); + } + + showActionSheet = () => { + ActionSheetIOS.showActionSheetWithOptions( + { + options: BUTTONS, + cancelButtonIndex: CANCEL_INDEX, + destructiveButtonIndex: DESTRUCTIVE_INDEX, + anchor: this.anchorRef.current + ? ReactNative.findNodeHandle(this.anchorRef.current) + : undefined, + }, + buttonIndex => { + this.setState({clicked: BUTTONS[buttonIndex]}); + }, + ); + }; +} + class ShareActionSheetExample extends React.Component< $FlowFixMeProps, $FlowFixMeState, @@ -129,7 +178,10 @@ class ShareActionSheetExample extends React.Component< }; } -class ShareScreenshotExample extends React.Component<{}, $FlowFixMeState> { +class ShareScreenshotExample extends React.Component< + $FlowFixMeProps, + $FlowFixMeState, +> { state = { text: '', }; @@ -171,11 +223,74 @@ class ShareScreenshotExample extends React.Component<{}, $FlowFixMeState> { }; } +class ShareScreenshotAnchorExample extends React.Component< + $FlowFixMeProps, + $FlowFixMeState, +> { + state = { + text: '', + }; + + anchorRef = React.createRef(); + + render() { + return ( + + + + Click to show the Share ActionSheet -> + + + HERE + + + {this.state.text} + + ); + } + + showShareActionSheet = () => { + // Take the snapshot (returns a temp file uri) + takeSnapshot('window') + .then(uri => { + // Share image data + ActionSheetIOS.showShareActionSheetWithOptions( + { + url: uri, + excludedActivityTypes: ['com.apple.UIKit.activity.PostToTwitter'], + anchor: this.anchorRef.current + ? ReactNative.findNodeHandle(this.anchorRef.current) + : undefined, + }, + error => Alert.alert('Error', error), + (completed, method) => { + let text; + if (completed) { + text = `Shared via ${method}`; + } else { + text = "You didn't share"; + } + this.setState({text}); + }, + ); + }) + .catch(error => Alert.alert('Error', error)); + }; +} + const style = StyleSheet.create({ button: { marginBottom: 10, fontWeight: '500', }, + anchorRow: { + flex: 1, + flexDirection: 'row', + justifyContent: 'space-between', + }, }); exports.title = 'ActionSheetIOS'; @@ -193,6 +308,12 @@ exports.examples = [ return ; }, }, + { + title: 'Show Action Sheet with anchor', + render(): React.Element { + return ; + }, + }, { title: 'Show Share Action Sheet', render(): React.Element { @@ -211,4 +332,10 @@ exports.examples = [ return ; }, }, + { + title: 'Share from Anchor', + render(): React.Element { + return ; + }, + }, ];