From 81d6afd91c2b1a1c84b79de50ff9800ef45e65c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Fri, 20 Oct 2023 17:06:56 +0200 Subject: [PATCH 1/3] feat(iOS): migrate deprecated UIMenuController to UIEditMenuInteraction --- .../Libraries/Text/Text/RCTTextView.mm | 46 +++++++++++++---- .../Text/RCTParagraphComponentView.mm | 49 ++++++++++++++----- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.mm b/packages/react-native/Libraries/Text/Text/RCTTextView.mm index 3f64e313ab58f1..ea650a07850b1e 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.mm +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.mm @@ -16,6 +16,14 @@ #import +#ifdef __IPHONE_16_0 +@interface RCTTextView () + +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); + +@end +#endif + @implementation RCTTextView { CAShapeLayer *_highlightLayer; UILongPressGestureRecognizer *_longPressGestureRecognizer; @@ -213,31 +221,49 @@ - (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 - UIMenuController *menuController = [UIMenuController sharedMenuController]; + if (@available(iOS 16.0, *)) { + CGPoint location = [gesture locationInView:self]; + UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; + if (_editMenuInteraction) { + [_editMenuInteraction presentEditMenuWithConfiguration:config]; + } + } else { + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) + UIMenuController *menuController = [UIMenuController sharedMenuController]; - if (menuController.isMenuVisible) { - return; - } + if (menuController.isMenuVisible) { + return; + } - if (!self.isFirstResponder) { - [self becomeFirstResponder]; - } + if (!self.isFirstResponder) { + [self becomeFirstResponder]; + } - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; + [menuController setTargetRect:self.bounds inView:self]; + [menuController setMenuVisible:YES animated:YES]; + } #endif } 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..c876e865d4417a 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,14 @@ using namespace facebook::react; +#ifdef __IPHONE_16_0 +@interface RCTParagraphComponentView () + +@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); + +@end +#endif + @implementation RCTParagraphComponentView { ParagraphShadowNode::ConcreteState::Shared _state; ParagraphAttributes _paragraphAttributes; @@ -211,31 +219,48 @@ - (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 - UIMenuController *menuController = [UIMenuController sharedMenuController]; - - if (menuController.isMenuVisible) { - return; + if (@available(iOS 16.0, *)) { + CGPoint location = [gesture locationInView:self]; + UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location]; + if (_editMenuInteraction) { + [_editMenuInteraction presentEditMenuWithConfiguration:config]; + } + } else { + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) + UIMenuController *menuController = [UIMenuController sharedMenuController]; + + if (menuController.isMenuVisible) { + return; + } + + if (!self.isFirstResponder) { + [self becomeFirstResponder]; + } + + [menuController setTargetRect:self.bounds inView:self]; + [menuController setMenuVisible:YES animated:YES]; } - - if (!self.isFirstResponder) { - [self becomeFirstResponder]; - } - - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; #endif } From 8afffa085b2c0b099f831af867ef18ba5a327e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Mon, 23 Oct 2023 12:32:04 +0200 Subject: [PATCH 2/3] fix: change if check to early returns --- .../Libraries/Text/Text/RCTTextView.mm | 24 +++++++++---------- .../Text/RCTParagraphComponentView.mm | 24 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.mm b/packages/react-native/Libraries/Text/Text/RCTTextView.mm index ea650a07850b1e..4bb6f8df6f8654 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.mm +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.mm @@ -249,21 +249,21 @@ - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture if (_editMenuInteraction) { [_editMenuInteraction presentEditMenuWithConfiguration:config]; } - } else { - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) - UIMenuController *menuController = [UIMenuController sharedMenuController]; - - if (menuController.isMenuVisible) { - return; - } + return; + } + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) + UIMenuController *menuController = [UIMenuController sharedMenuController]; - if (!self.isFirstResponder) { - [self becomeFirstResponder]; - } + if (menuController.isMenuVisible) { + return; + } - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; + if (!self.isFirstResponder) { + [self becomeFirstResponder]; } + + [menuController setTargetRect:self.bounds inView:self]; + [menuController setMenuVisible:YES animated:YES]; #endif } 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 c876e865d4417a..650800088decfd 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -246,21 +246,21 @@ - (void)handleLongPress:(UILongPressGestureRecognizer *)gesture if (_editMenuInteraction) { [_editMenuInteraction presentEditMenuWithConfiguration:config]; } - } else { - // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) - UIMenuController *menuController = [UIMenuController sharedMenuController]; - - if (menuController.isMenuVisible) { - return; - } + return; + } + // TODO: Adopt showMenuFromRect (necessary for UIKitForMac) + UIMenuController *menuController = [UIMenuController sharedMenuController]; - if (!self.isFirstResponder) { - [self becomeFirstResponder]; - } + if (menuController.isMenuVisible) { + return; + } - [menuController setTargetRect:self.bounds inView:self]; - [menuController setMenuVisible:YES animated:YES]; + if (!self.isFirstResponder) { + [self becomeFirstResponder]; } + + [menuController setTargetRect:self.bounds inView:self]; + [menuController setMenuVisible:YES animated:YES]; #endif } From adabfb903f7c0be3b7de577161c54a773ce00b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Wed, 25 Oct 2023 16:48:05 +0200 Subject: [PATCH 3/3] fix: remove unnecessary ifdef --- packages/react-native/Libraries/Text/Text/RCTTextView.mm | 2 -- .../Mounting/ComponentViews/Text/RCTParagraphComponentView.mm | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/react-native/Libraries/Text/Text/RCTTextView.mm b/packages/react-native/Libraries/Text/Text/RCTTextView.mm index 4bb6f8df6f8654..dc38bb381565aa 100644 --- a/packages/react-native/Libraries/Text/Text/RCTTextView.mm +++ b/packages/react-native/Libraries/Text/Text/RCTTextView.mm @@ -16,13 +16,11 @@ #import -#ifdef __IPHONE_16_0 @interface RCTTextView () @property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); @end -#endif @implementation RCTTextView { CAShapeLayer *_highlightLayer; 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 650800088decfd..682532ac2829b7 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -24,13 +24,11 @@ using namespace facebook::react; -#ifdef __IPHONE_16_0 @interface RCTParagraphComponentView () @property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0)); @end -#endif @implementation RCTParagraphComponentView { ParagraphShadowNode::ConcreteState::Shared _state;