fix(ios): keep recycled text views at their container width

Apply the minimal candidate from #6364 and cover recycled geometry in the native probe. Document that the probe does not reproduce mounted Fabric clipping.

Co-authored-by: Oxxx <103127477+omcdowell@users.noreply.github.com>
This commit is contained in:
Mohamed Boudra
2026-10-09 22:24:28 +02:00
co-authored by Oxxx
parent b89ebbede6
commit f521abfb24
4 changed files with 52 additions and 17 deletions
@@ -13,6 +13,11 @@ bash packages/app/e2e/mobile/native-text-reflow/ios.sh
The probe constructs the installed `RNUITextView`, fills its real UIKit text view,
and resizes its content container from 500 to 240 and back to 500 points. It
asserts the inner frame follows both widths and the same selection survives.
It then recycles the view the way Fabric's pool does and remounts it 30 points
wider and at the same size; the inner frame must still match the container.
A zeroed frame on recycle comes back 30 or 0 points wide. This assertion checks
the intermediate recycled geometry; it does not reproduce visible clipping in a
mounted Fabric paragraph or exercise the subsequent `drawRect` call.
It uses public UIView/UITextView operations, without mocking React Native.
LLDB detaches after the probe. An assertion failure or evaluation error fails the
shell command; the log distinguishes them.
@@ -39,14 +39,28 @@ def __lldb_init_module(debugger, _internal_dict):
container.frame = frame;
[container layoutIfNeeded];
CGFloat wide = text.frame.size.width;
auto keptSelection = text.selectedRange;
// Fabric recycles the view: the container keeps its size in the pool,
// and the next paragraph mounts it 30 points wider (a list-item paragraph
// reused by a full-width one) or at the same size.
[host performSelector:@selector(prepareForRecycle)];
frame.size.width = 530;
container.frame = frame;
[container layoutIfNeeded];
CGFloat reusedWider = text.frame.size.width;
[host performSelector:@selector(prepareForRecycle)];
container.frame = frame;
[container layoutIfNeeded];
CGFloat reusedSame = text.frame.size.width;
BOOL ok = text != nil && narrow == 240 && wide == 500 &&
selection.location == text.selectedRange.location &&
selection.length == text.selectedRange.length &&
reusedWider == 530 && reusedSame == 530 &&
selection.location == keptSelection.location &&
selection.length == keptSelection.length &&
selection.location == 12 && selection.length == 9;
[NSString stringWithFormat:@"NATIVE_REFLOW_%@ narrow=%.0f wide=%.0f selection=%lu:%lu",
ok ? @"PASS" : @"FAIL", narrow, wide,
(unsigned long)text.selectedRange.location,
(unsigned long)text.selectedRange.length];
[NSString stringWithFormat:@"NATIVE_REFLOW_%@ narrow=%.0f wide=%.0f recycled=%.0f,%.0f selection=%lu:%lu",
ok ? @"PASS" : @"FAIL", narrow, wide, reusedWider, reusedSame,
(unsigned long)keptSelection.location,
(unsigned long)keptSelection.length];
})''',
options,
)
@@ -15,4 +15,4 @@ xcrun lldb --batch -p "$PASEO_NATIVE_TEXT_REFLOW_PID" \
-o "process detach" -o quit > "$PASEO_NATIVE_TEXT_REFLOW_LOG" 2>&1
grep '^NATIVE_REFLOW_' "$PASEO_NATIVE_TEXT_REFLOW_LOG"
grep -q '^NATIVE_REFLOW_PASS narrow=240 wide=500 selection=12:9$' "$PASEO_NATIVE_TEXT_REFLOW_LOG"
grep -q '^NATIVE_REFLOW_PASS narrow=240 wide=500 recycled=530,530 selection=12:9$' "$PASEO_NATIVE_TEXT_REFLOW_LOG"