Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ - (void)_updateTextViewFrame
CGRect textViewFrame = self.bounds;
CGRect drawingFrame = RCTCGRectFromRect(_layoutMetrics.getContentFrame());

// Lay out the text in the size the paragraph has in the shadow tree rather than in the current frame, which
// LayoutAnimation interpolates. Otherwise, line breaks change on every frame and words jump between lines.
// The text view clips the text to the current bounds.
if (_textView.state) {
drawingFrame.size = RCTCGSizeFromSize(_textView.state->getData().layoutSize);
}

if (ReactNativeFeatureFlags::enableIOSCompressedTextFrameAdjustment() && _textView.state &&
drawingFrame.size.height > 0) {
const auto &stateData = _textView.state->getData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ void ParagraphShadowNode::updateStateIfNeeded(const Content& content) {
auto& state = getStateData();

react_native_assert(textLayoutManager_);
#ifdef ANDROID
if (state.attributedString == content.attributedString) {
return;
}
Expand All @@ -187,6 +188,20 @@ void ParagraphShadowNode::updateStateIfNeeded(const Content& content) {
content.attributedString,
content.paragraphAttributes,
textLayoutManager_});
#else
auto layoutSize = getLayoutMetrics().getContentFrame().size;
if (state.attributedString == content.attributedString &&
state.layoutSize == layoutSize) {
return;
}

setStateData(
ParagraphState{
content.attributedString,
content.paragraphAttributes,
textLayoutManager_,
layoutSize});
#endif
}

MeasuredPreparedTextLayout* ParagraphShadowNode::findUsableLayout() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ class ParagraphShadowNode
void updateStateIfNeeded(const Content &content, const MeasuredPreparedTextLayout &layout);

/*
* Creates a `State` object (with `AttributedText` and
* `TextLayoutManager`) if needed.
* Creates a `State` object (with `AttributedText`, `TextLayoutManager` and,
* outside of Android, the size of the content frame) if needed.
*/
void updateStateIfNeeded(const Content &content);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <react/cxxstableapi/FrameworksGuard.h>

#include <React/Debug.h>
#include <React/Graphics.h>
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/attributedstring/ParagraphAttributes.h>
#include <react/renderer/textlayoutmanager/TextLayoutManager.h>
Expand Down Expand Up @@ -39,6 +40,14 @@ class ParagraphState final {
* `AttributedString`.
*/
std::weak_ptr<const TextLayoutManager> layoutManager;

/*
* Size of the content frame that the paragraph was laid out with.
* The mounted view can temporarily have a different size (e.g. while
* LayoutAnimation interpolates its frame), so it lays out text in this size
* to keep line breaks stable.
*/
Size layoutSize;
};

} // namespace facebook::react
Loading