react-native/Libraries/ActionSheetIOS/RCTActionSheetManager.m

170 lines
5.1 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTActionSheetManager.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#import "RCTBridge.h"
#import "RCTUIManager.h"
@interface RCTActionSheetManager () <UIActionSheetDelegate>
@end
@implementation RCTActionSheetManager
{
NSMutableDictionary *_callbacks;
}
RCT_EXPORT_MODULE()
- (instancetype)init
{
if ((self = [super init])) {
_callbacks = [NSMutableDictionary new];
}
return self;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options
failureCallback:(__unused RCTResponseSenderBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{
if (RCTRunningInAppExtension()) {
RCTLogError(@"Unable to show action sheet from app extension");
return;
}
UIActionSheet *actionSheet = [UIActionSheet new];
actionSheet.title = options[@"title"];
for (NSString *option in options[@"options"]) {
[actionSheet addButtonWithTitle:option];
}
if (options[@"destructiveButtonIndex"]) {
actionSheet.destructiveButtonIndex = [options[@"destructiveButtonIndex"] integerValue];
}
if (options[@"cancelButtonIndex"]) {
actionSheet.cancelButtonIndex = [options[@"cancelButtonIndex"] integerValue];
}
actionSheet.delegate = self;
_callbacks[RCTKeyForInstance(actionSheet)] = successCallback;
UIWindow *appWindow = RCTSharedApplication().delegate.window;
if (appWindow == nil) {
RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options);
return;
}
[actionSheet showInView:appWindow];
}
RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
failureCallback:(RCTResponseSenderBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{
NSMutableArray *items = [NSMutableArray array];
NSString *message = [RCTConvert NSString:options[@"message"]];
if (message) {
[items addObject:message];
}
NSURL *URL = [RCTConvert NSURL:options[@"url"]];
if (URL) {
[items addObject:URL];
}
if (items.count == 0) {
failureCallback(@[@"No `url` or `message` to share"]);
return;
}
if (RCTRunningInAppExtension()) {
failureCallback(@[@"Unable to show action sheet from app extension"]);
return;
}
UIActivityViewController *share = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
UIViewController *ctrl = RCTSharedApplication().delegate.window.rootViewController;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
if (![UIActivityViewController instancesRespondToSelector:@selector(setCompletionWithItemsHandler:)]) {
// Legacy iOS 7 implementation
share.completionHandler = ^(NSString *activityType, BOOL completed) {
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
};
} else
#endif
{
// iOS 8 version
share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) {
if (activityError) {
failureCallback(@[RCTNullIfNil(activityError.localizedDescription)]);
} else {
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
}
};
}
/*
* 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.
*/
if ([share respondsToSelector:@selector(popoverPresentationController)]) {
share.popoverPresentationController.sourceView = ctrl.view;
NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]];
if (anchorViewTag) {
UIView *anchorView = [self.bridge.uiManager viewForReactTag:anchorViewTag];
share.popoverPresentationController.sourceRect = [anchorView convertRect:anchorView.bounds toView:ctrl.view];
} else {
CGRect sourceRect = CGRectMake(ctrl.view.center.x, ctrl.view.center.y, 1, 1);
share.popoverPresentationController.sourceRect = sourceRect;
share.popoverPresentationController.permittedArrowDirections = 0;
}
}
[ctrl presentViewController:share animated:YES completion:nil];
}
#pragma mark UIActionSheetDelegate Methods
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *key = RCTKeyForInstance(actionSheet);
RCTResponseSenderBlock callback = _callbacks[key];
if (callback) {
callback(@[@(buttonIndex)]);
[_callbacks removeObjectForKey:key];
} else {
RCTLogWarn(@"No callback registered for action sheet: %@", actionSheet.title);
}
[RCTSharedApplication().delegate.window makeKeyWindow];
}
#pragma mark Private
static NSString *RCTKeyForInstance(id instance)
{
return [NSString stringWithFormat:@"%p", instance];
}
@end