Skip to content

Commit

Permalink
Fixing ActionSheetIOS position after rotation on tablet (#22738)
Browse files Browse the repository at this point in the history
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: #22738

Differential Revision: D13582810

Pulled By: PeteTheHeat

fbshipit-source-id: a93065284b02efc41ae7378465521330a828a126
  • Loading branch information
stigi authored and facebook-github-bot committed Jan 4, 2019
1 parent 7de2f77 commit ac39795
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 32 deletions.
45 changes: 15 additions & 30 deletions Libraries/ActionSheetIOS/RCTActionSheetManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
131 changes: 129 additions & 2 deletions RNTester/js/ActionSheetIOSExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ class ActionSheetExample extends React.Component<Props, State> {
};
}

class ActionSheetTintExample extends React.Component<{}, $FlowFixMeState> {
class ActionSheetTintExample extends React.Component<
$FlowFixMeProps,
$FlowFixMeState,
> {
state = {
clicked: 'none',
};
Expand Down Expand Up @@ -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 (
<View>
<View style={style.anchorRow}>
<Text style={style.button}>
Click there to show the ActionSheet ->
</Text>
<Text
onPress={this.showActionSheet}
style={style.button}
ref={this.anchorRef}>
HERE
</Text>
</View>
<Text>Clicked button: {this.state.clicked}</Text>
</View>
);
}

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,
Expand Down Expand Up @@ -129,7 +178,10 @@ class ShareActionSheetExample extends React.Component<
};
}

class ShareScreenshotExample extends React.Component<{}, $FlowFixMeState> {
class ShareScreenshotExample extends React.Component<
$FlowFixMeProps,
$FlowFixMeState,
> {
state = {
text: '',
};
Expand Down Expand Up @@ -171,11 +223,74 @@ class ShareScreenshotExample extends React.Component<{}, $FlowFixMeState> {
};
}

class ShareScreenshotAnchorExample extends React.Component<
$FlowFixMeProps,
$FlowFixMeState,
> {
state = {
text: '',
};

anchorRef = React.createRef();

render() {
return (
<View>
<View style={style.anchorRow}>
<Text style={style.button}>
Click to show the Share ActionSheet ->
</Text>
<Text
onPress={this.showShareActionSheet}
style={style.button}
ref={this.anchorRef}>
HERE
</Text>
</View>
<Text>{this.state.text}</Text>
</View>
);
}

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';
Expand All @@ -193,6 +308,12 @@ exports.examples = [
return <ActionSheetTintExample />;
},
},
{
title: 'Show Action Sheet with anchor',
render(): React.Element<any> {
return <ActionSheetAnchorExample />;
},
},
{
title: 'Show Share Action Sheet',
render(): React.Element<any> {
Expand All @@ -211,4 +332,10 @@ exports.examples = [
return <ShareScreenshotExample />;
},
},
{
title: 'Share from Anchor',
render(): React.Element<any> {
return <ShareScreenshotAnchorExample />;
},
},
];

0 comments on commit ac39795

Please sign in to comment.