Skip to content

fix: support partial text selection in <Text selectable> - #58363

Open
raajnadar wants to merge 1 commit into
react:mainfrom
raajnadar:fix/ios-partial-text-selection
Open

fix: support partial text selection in <Text selectable>#58363
raajnadar wants to merge 1 commit into
react:mainfrom
raajnadar:fix/ios-partial-text-selection

Conversation

@raajnadar

@raajnadar raajnadar commented Sep 6, 2026

Copy link
Copy Markdown

Summary:

Fixes #13938, open since 2017. Also covers #55187, which reports the same gap as an iOS 26 regression‚ it is not a regression. iOS selection has always copied the whole block.

On iOS, <Text selectable> installed a long press recogniser and a UIEditMenuInteraction whose single action copied the entire paragraph. There was no highlight, no drag handles, and no way to take a substring. Android has supported all three since the prop was introduced.

Approach

A selectable paragraph now renders through a non-editable UITextView, which is what provides selection, drag handles and the edit menu on iOS.

The care here is in the layout. React Native measures text off the main thread and the drawn result has to match that measurement exactly, so the text view is never allowed to lay anything out itself. RCTTextLayoutManager already builds a plain TextKit stack (NSTextStorage, NSLayoutManager, NSTextContainer), and UITextView accepts an existing container at initialisation. Handing it the container the paragraph was measured with makes the layout identical by construction rather than by coincidence:

NSTextStorage *textStorage = [_selectionLayoutManager textStorageForNSAttributedString:attributedText
                                                                   paragraphAttributes:_paragraphAttributes
                                                                                  size:contentFrame.size];
NSTextContainer *textContainer = textStorage.layoutManagers.firstObject.textContainers.firstObject;
_selectableTextView = [[RCTSelectableTextView alloc] initWithFrame:contentFrame textContainer:textContainer];

The text view is also pinned so it cannot reflow: widthTracksTextView and heightTracksTextView are off, lineFragmentPadding is zero, and both insets are zero. UIKit performs the selection; React Native keeps the layout.

Scope

  • Only <Text selectable> takes this path. Every other paragraph draws exactly as before.
  • No new native component, no feature flag, no JavaScript change, no codegen.
  • tvOS is unchanged; the new code sits behind !TARGET_OS_TV.
  • Three files, +124 / ‚àí62. Most of the deletion is the old whole-block copy action.

Behaviour change to be aware of

Copy now copies the selected range rather than the whole paragraph. That is the point of the fix, but an app relying on "long press copies everything" will see a difference.

Changelog:

[IOS] [FIXED] - <Text selectable> now supports partial text selection, with drag handles and the standard edit menu, instead of copying the whole block

Test Plan:

Tested on RNTester on an iPhone 16 Pro simulator, iOS 18.6, built with:

xcodebuild build -workspace RNTesterPods.xcworkspace -scheme RNTester -configuration Debug \
  -destination 'platform=iOS Simulator,name=iPhone 16 Pro'

Before and after were captured on the same simulator, with the same paragraph and the same gesture

before-copy-only after-partial-selection
Before After
Long press, then drag One Copy bubble for the whole block, no highlight, no handles A substring selected, both drag handles work

Each remaining case rendered a selectable paragraph beside an identical non-selectable control, so any difference is attributable to this change.

Case Result
Edit menu Copy, Look Up and Translate, acting on the selected range
onPress on a nested <Text> Fires on a short tap; tap counter matched the control exactly
numberOfLines={2} Same truncation point and ellipsis as the control
Inline <View> child Same position and text flow as the control
Console No warnings or errors

yarn format reports no changes on the three files.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 6, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Click and hold to select text using a text component

1 participant