react-native: Component with over ~500 lines won't render [iOS]
- I have reviewed the documentation
- I have searched existing issues
- I am using the latest React Native version
A Text
component with about 500-600+ lines of text renders completely blank.
Environment
Environment: OS: macOS Sierra 10.12.6 Node: 9.3.0 Yarn: Not Found npm: 5.8.0 Watchman: 4.9.0 Xcode: Xcode 9.2 Build version 9C40b Android Studio: 3.0 AI-171.4443003
Packages: (wanted => installed) react: 16.3.1 => 16.3.1 react-native: 0.55.4 => 0.55.4
Steps to Reproduce
import {
Text,
View,
ScrollView
} from 'react-native';
const text = ".\n".repeat(600)
export default class App extends Component {
render() {
return (
<View style={{
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
backgroundColor: '#F5FCFF',
}}>
<ScrollView style={{flex: 1}}>
<Text>{text}</Text>
</ScrollView>
</View>
);
}
}
At 600 lines there’s no rendering of the text component, at 200 it renders fine.
Expected Behavior
I expected the normal behavior of a Text
component embedded in a ScrollView
. For an example, render the above code with a repeat value of 200 instead of 600.
Actual Behavior
Nothing is rendered, just a blank area where the text should be.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 19 (7 by maintainers)
Commits related to this issue
- Use CALayers to draw text (#24387) Summary: The current technique we use to draw text uses linear memory, which means that when text is too long the UIView layer is unable to draw it. This causes the... — committed to facebook/react-native by janicduplessis 5 years ago
- Use CALayers to draw text (#24387) Summary: The current technique we use to draw text uses linear memory, which means that when text is too long the UIView layer is unable to draw it. This causes the... — committed to M-i-k-e-l/react-native by janicduplessis 5 years ago
As a workaround using
works fine.
I’ve looked into it a bit and we don’t seem to be using UILabel. We are using
NSLayoutManager
to draw the text inside aUIView
subclass. Not sure what magicUITextView
does to be able to render this properly.I’ve also noticed that the issue does not happen on a physical device (iPhone XS), or maybe the number of lines required to trigger the bug is bigger.
So after investigating this for a very long time I managed to reduce the issue to the usage of
[drawRect:]
and a frame with either width or height greater than 5000 (the exact number is between 5k and 6k). Seems like a bug in UIKit, here’s a repro:This example would result in a fully black screen (even the white background doesn’t render). Removing
drawRect
fromTestView
would cause the view to start rendering properly again (cover the screen as a white view). Reducing width and height to 5k would cause everything to work again (white screen with red square).I tried exploring a bunch of workarounds and managed to find a solution that works! I noticed that text rendering works when using a CATextLayer instead of
drawRect
. I also went and looked at how ComponentKit renders text and found that it uses a customCALayer
subclass and draws the text usingNSLayoutManager
indrawInContext
(https://github.com/facebook/componentkit/blob/master/ComponentTextKit/CKTextComponentLayer.mm#L100, https://github.com/facebook/componentkit/blob/master/ComponentTextKit/TextKit/CKTextKitRenderer.mm#L101) . This is very similar to the setup we have inRCTTextView.m
and I managed to get a proof of concept working and rendering text properly.Looking into cleaning this up and opening a PR to fix this.
Edit: This doesn’t actually fixes the bug but increases the amount of lines that can be rendered. The number of lines required to trigger the bug also seem to depend on the device. For the simulator the number is around 500 lines, for an iPhone XS the number is around 85k lines. Using
CALayer
seems to 2-3x the number of lines that can be rendered.Edit 2: Got a fully working solution using CATiledLayer.
@jackthias good clarification, it sounds like the action here should be to: