Skip to content

fix(ios): replace per-cell UITextView with single drawRect pass for tables#418

Draft
hryhoriiK97 wants to merge 1 commit into
mainfrom
fix/ios-table-drawrect-apphang
Draft

fix(ios): replace per-cell UITextView with single drawRect pass for tables#418
hryhoriiK97 wants to merge 1 commit into
mainfrom
fix/ios-table-drawrect-apphang

Conversation

@hryhoriiK97

Copy link
Copy Markdown
Collaborator

What/Why?

Replace per-cell UITextView allocation in iOS table rendering with a single drawRect: pass, eliminating multi-second AppHangs on large tables.

Problem: Each table cell allocated a full UITextView with its own TextKit stack (NSTextStorage, NSLayoutManager, NSTextContainer). Setting the frame triggered synchronous typesetting via ensureLayoutForBounds: per cell. A 200×7 table = 1,400 UITextViews blocking the main thread — 10–85s hangs in production, including fatal watchdog kills. Both the render path (addTextToCell:) and shadow-node measurement (renderMarkdownSynchronously:) were affected.

Fix: New ENRMTableIOSGridView draws all cells in one drawRect: pass using NSAttributedString.drawWithRect: — same approach macOS already uses via ENRMTableGridView. Link handling reimplemented with on-demand TextKit hit-testing (only on tap, not per-cell on render). Shadow-node measurement now creates one lightweight view instead of 1,400 UITextViews.

Testing

PR Checklist

  • Code compiles and runs on iOS
  • Code compiles and runs on Android
  • Updated documentation/README if applicable
  • Ran example app to verify changes
  • E2E tests are passing
  • Required E2E tests have been added (if applicable)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes iOS table rendering by replacing per-cell UITextView creation with a single custom drawRect:-based grid view, aiming to eliminate main-thread hangs on large tables while keeping link interaction support.

Changes:

  • Reworks iOS table rendering in TableContainerView to build row data and delegate drawing to a new ENRMTableIOSGridView.
  • Adds ENRMTableIOSGridView / ENRMTableIOSRowData to render the whole table in one drawRect: pass and perform on-demand link hit-testing on tap/long-press.
  • Updates iOS “new rows” animation to use a view-level cross-dissolve instead of per-cell alpha animations.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
packages/react-native-enriched-markdown/ios/views/TableContainerView.m Switches iOS tables to a single grid view renderer; builds per-row data for drawing and updates row animation hook.
packages/react-native-enriched-markdown/ios/views/ENRMTableIOSGridView.m New iOS grid view implementation (drawing + gesture handling + link hit-testing).
packages/react-native-enriched-markdown/ios/views/ENRMTableIOSGridView.h Public interface for the new iOS grid view + row data container.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +168 to +173
id linkValue = [text attribute:NSLinkAttributeName atIndex:charIndex effectiveRange:NULL];
if ([linkValue isKindOfClass:[NSURL class]])
return [(NSURL *)linkValue absoluteString];
if ([linkValue isKindOfClass:[NSString class]])
return linkValue;
return nil;
Comment on lines +161 to +166
NSUInteger glyphIndex = [layoutManager glyphIndexForPoint:local
inTextContainer:textContainer
fractionOfDistanceThroughGlyph:&fraction];
NSUInteger charIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
if (charIndex >= text.length)
return nil;
CGFloat rowHeight = [_rowHeights[r] doubleValue];
CGFloat xOffset = 0;

for (NSUInteger c = 0; c < rowData.cellTexts.count; c++) {
Comment on lines +48 to +55
_tableRows = rows;
_columnWidths = columnWidths;
_rowHeights = rowHeights;
_borderColor = borderColor;
_borderWidth = borderWidth;
_horizontalCellPadding = horizontalCellPadding;
_verticalCellPadding = verticalCellPadding;
[self setNeedsDisplay];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants