From 98d15cefbd944e9db7e5f91be27a4f731c7405be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Fri, 27 Oct 2023 11:11:19 -0700 Subject: [PATCH] feat(iOS): migrate deprecated UIMenuController to UIEditMenuInteraction (#41125) Summary: The goal of this PR is to migrate deprecated `UIMenuController` to `UIEditMenuInteraction`. `UIMenuController` has been deprecated in iOS 16 and for that reason it's not available for VisionOS. https://github.com/facebook/react-native/assets/52801365/fed994be-d444-462a-9ed0-39b50531425d bypass-github-export-checks [IOS] [CHANGED] - Migrate RCTTextView to UIEditMenuInteraction Pull Request resolved: https://github.com/facebook/react-native/pull/41125 Test Plan: Launch RNTester and check for "Selectable Text" example and check that it works for iOS 16/17. Reviewed By: javache Differential Revision: D50551016 Pulled By: cipolleschi fbshipit-source-id: 558ecc5a04a5daa9c4360fabddcab28fba72a323 --- .../Libraries/Text/Text/RCTTextView.m | 25 ++++++++++++++--- .../Text/RCTParagraphComponentView.mm | 27 ++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.m b/packages/react-native/Libraries/Text/Text/RCTTextView.m index 9ac677b4e90b3d..4868840ed4fc61 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.m +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.m @@ -37,11 +37,18 @@ - (BOOL)canBecomeKeyView @end -@interface RCTTextView () -@end +#endif // macOS] + +#if !TARGET_OS_OSX // [macOS] +@interface RCTTextView () +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); +#else // [macOS +@interface RCTTextView () #endif // macOS] +@end + #import @implementation RCTTextView { @@ -358,6 +365,10 @@ - (void)enableContextMenu { _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; + if (@available(iOS 16.0, *)) { + _editMenuInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self]; + [self addInteraction:_editMenuInteraction]; + } [self addGestureRecognizer:_longPressGestureRecognizer]; } @@ -369,8 +380,16 @@ - (void)disableContextMenu - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) #if !TARGET_OS_UIKITFORMAC + if (@available(iOS 16.0, *)) { + CGPoint location = [gesture locationInView:self]; + UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; + if (_editMenuInteraction) { + [_editMenuInteraction presentEditMenuWithConfiguration:config]; + } + return; + } + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) UIMenuController *menuController = [UIMenuController sharedMenuController]; if (menuController.isMenuVisible) { diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index eddff908cfaf88..cb79a4b3b6249d 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -27,6 +27,14 @@ using namespace facebook::react; +#if !TARGET_OS_OSX // [macOS] +@interface RCTParagraphComponentView () + +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); + +@end +#endif // [macOS] + @implementation RCTParagraphComponentView { ParagraphShadowNode::ConcreteState::Shared _state; ParagraphAttributes _paragraphAttributes; @@ -223,19 +231,36 @@ - (void)enableContextMenu { _longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)]; + + if (@available(iOS 16.0, *)) { + _editMenuInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self]; + [self addInteraction:_editMenuInteraction]; + } [self addGestureRecognizer:_longPressGestureRecognizer]; } - (void)disableContextMenu { [self removeGestureRecognizer:_longPressGestureRecognizer]; + if (@available(iOS 16.0, *)) { + [self removeInteraction:_editMenuInteraction]; + _editMenuInteraction = nil; + } _longPressGestureRecognizer = nil; } - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture { - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) #if !TARGET_OS_UIKITFORMAC + if (@available(iOS 16.0, *)) { + CGPoint location = [gesture locationInView:self]; + UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; + if (_editMenuInteraction) { + [_editMenuInteraction presentEditMenuWithConfiguration:config]; + } + return; + } + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) UIMenuController *menuController = [UIMenuController sharedMenuController]; if (menuController.isMenuVisible) {