Skip to content

Conversation

@IvanIhnatsiuk
Copy link
Contributor

@IvanIhnatsiuk IvanIhnatsiuk commented Dec 18, 2025

Summary

This PR simplifies and significantly optimizes text measurement + corrects textKit measurements.

Closes #332

Problems:

  1. We had a bug that contentInsets were incorrectly applied, and padding was treated as margin.
  2. We had asynchronous layout calculations that caused a small delay in the initial mount.
  3. We had some workarounds to make content scrollable by setting textContentSize manually.
  4. Scroll position is lost if we navigate back to the screen with the editor

All these problems were because we used the setContentView method, which is not a UIKit method(it was a RN method) that required proper children caculation. However, in our scenario, we just need to know the current content size, and UIKit can calculate the UIScrollView bounds and text content automatically.

Why did it happen?

InputTextView Content Size Measurement Issue

Problem Description

The issue was related to our InputTextView.

InputTextView always had zero bounds ({0,0}) during layout, which meant that TextKit did not know the actual size of the text container. As a result, TextKit attempted to measure contentSize using an invalid (zero-width) container, which led to incorrect text measurement and layout behavior.

This behavior can be verified by placing a breakpoint in the method responsible for computing contentSize inside InputTextView: at that moment, TextKit had no knowledge of the real container size.


Root Cause

  • updateLayoutMetrics did not properly apply geometry to InputTextView
  • The view’s frame/bounds remained zero
  • textContainer.size did not reflect the actual width of the component
  • TextKit was asked to measure text before receiving the final container size

Because of this, contentSize was calculated incorrectly, which caused issues such as:

  • incorrect height calculation
  • broken initial layout
  • scrolling not activating properly

Benchmarks

for the text content like:

const html =
  '<html>' +
  Array(1000)
    .fill(
      `<h1><u>Test</u></h1>
      <ul><li>First item</li></ul>
      <h2>Test h2</h2>
      <p>This is a <b>bold</b> statement with a <a href="https://www.example.com">link</a> and an image below:</p>
      <ol>
        <li>First ordered item</li>
        <li>Second ordered item with <i>italic</i> text</li>
      </ol>
      <blockquote>This is a blockquote.</blockquote>
      <p>Here is some inline code: <code>const x = 10;</code></p>
      `
    )
    .join('') +
  '</html>';
Scenario Before (ms) After (ms)
Measure size on initial mount 408 141
Single character change 764 0.007

Test Plan

  1. Set a huge default value using
const html =
  '<html>' +
  Array(100)
    .fill(`<h1><u>Test</u></h1><ul><li>First item</li></ul><h2>Test h2</h2>`)
    .join('') +
  '</html>';
  1. Test that scrolling/typing is working well

Provide clear steps so another contributor can reproduce the behavior or verify the feature works.
For example:

  • Steps to reproduce the bug (if this is a bug fix)
  • Steps to verify the new feature
  • Expected vs actual results
  • Any special conditions or edge cases to test

Screenshots / Videos

Initial layout + scrolling

Screen.Recording.2025-12-21.at.18.40.18.mov

Long single paragraph

Screen.Recording.2025-12-21.at.18.45.17.mov

New line insertion:

Please watch these videos at 0.25x speed.

Before

Screen.Recording.2025-12-21.at.18.48.49.mov

After

Screen.Recording.2025-12-21.at.18.46.10.mov

Content size layout measurement (Before)

Uploading Screen Recording 2025-12-21 at 18.55.16.mov…

Error that content size is ambiguous

Screenshot 2025-12-21 at 18 56 22

Compatibility

OS Implemented
iOS
Android

@szydlovsky
Copy link
Collaborator

szydlovsky commented Dec 18, 2025

Hey @IvanIhnatsiuk the changes look promising but we need to make sure absolutely no regressions are introduced.
Can you please check the reproductions from these three PR's:

We cannot proceed with the PR no matter how big the performance gains if any of these re-emerge.

If it comes to the measuring part, I'll check it on my end.

@IvanIhnatsiuk
Copy link
Contributor Author

@szydlovsky Sure and it's obviously clear to me 😄 . I will test all of these scenarios and comeback with update 🙂

@IvanIhnatsiuk
Copy link
Contributor Author

@szydlovsky

  1. It is unclear what I should test
  2. Works as expected
Screen.Recording.2025-12-18.at.17.57.54.mov
  1. Again, it works:
Screen.Recording.2025-12-18.at.18.01.27.mov

@szydlovsky
Copy link
Collaborator

@IvanIhnatsiuk okay, we anyway want to test it out really thoroughly. Might be as late as at the beginning of the new year so we'll need some patience here 🙏

@IvanIhnatsiuk IvanIhnatsiuk force-pushed the fix/prope-layout-calculation branch from f72acb5 to 4caf62b Compare December 21, 2025 16:53
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context (5.6.2):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please, let me know if you want to keep separate screens in the example app or I should remove all of this logic.

NOTE:

Initially, I added it to simplify testing on your side

Copy link
Collaborator

Choose a reason for hiding this comment

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

It should be removed. If we considered extending example app, we would like to this separately

Copy link
Contributor Author

@IvanIhnatsiuk IvanIhnatsiuk Dec 22, 2025

Choose a reason for hiding this comment

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

@exploIF Okay, then I will keep it for testing purposes. Once you test it and confirm that there are no issues, I will revert these changes

@IvanIhnatsiuk IvanIhnatsiuk changed the title fix: proper layout calculation fix(iOS): proper layout calculation Dec 22, 2025
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.

Padding seems to be applied as margin

3 participants