From e08a1973f67d85acc157111c749c43572469e4c2 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. ## Recording https://github.com/facebook/react-native/assets/52801365/fed994be-d444-462a-9ed0-39b50531425d bypass-github-export-checks ## Changelog: [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.mm | 26 ++++++++++++++++++- .../Text/RCTParagraphComponentView.mm | 25 +++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.mm b/packages/react-native/Libraries/Text/Text/RCTTextView.mm index 3f64e313ab58f1..bcfb5003ee94e8 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.mm +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.mm @@ -16,6 +16,12 @@ #import +@interface RCTTextView () + +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); + +@end + @implementation RCTTextView { CAShapeLayer *_highlightLayer; UILongPressGestureRecognizer *_longPressGestureRecognizer; @@ -213,19 +219,37 @@ - (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) { 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 27f52dd15150dc..fce427848a9c11 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -24,6 +24,12 @@ using namespace facebook::react; +@interface RCTParagraphComponentView () + +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); + +@end + @implementation RCTParagraphComponentView { ParagraphShadowNode::ConcreteState::Shared _state; ParagraphAttributes _paragraphAttributes; @@ -211,19 +217,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) {