-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<Text> Component with over ~500 lines won't render [iOS] #19453
Comments
This comment has been minimized.
This comment has been minimized.
Sorry, I accidentally tapped the enter key while I was still typing the title. |
This is tagged as no environment info, but there's clearly environment info in the post. Am I missing anything? That's the output of the command. |
Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions. |
Still having this issue in RN 0.57. Had to write a chunking routine to break large strings into 10k chunks and rendered in sequential Text components, which gets around the issue for us. Does not happen on Android builds, only IOS, including hardware 6, X and XR |
The magic number appears to be 490 characters. Am taking a look at this. |
|
I can't tell if it is Yoga or NSTextContainer, but the new magic number is 8192. You cannot return a height greater than 8192 from RCTTextShadowViewMeasure when the height of an NSTextContainer is greater than 8203. If you limit the measure to max out at 8192 artificially, it will draw (but is obviously not all of the text). This is puzzling, as I cannot tell who is really at fault. It may make sense to try this with a standalone iOS project. |
Related: #22713 |
Hey, I haven't touched this in a while, because I found a workaround. Here's what I found while digging into the issue way back when I discovered this issue. It appears to be a problem with the Cocoa UILabel Component for iOS which is intended only for short blocks of text. This is the component that the React Native uses to implement the component on iOS. Which is generally fine, until you run into large rendering of text, in which case the Label no longer suffices. In a native iOS application, this problem would be avoided by the use of a read-only UITextView, which--unlike UILabel--is built to support large texts. You can get your RN project to use the UITextView object instead of the UILabel object by using instead of and setting the appropriate props to make it read-only (and not select-able, if that matters). The issue I ran into was that the component doesn't (or at least didn't, it'd been many months since I've checked) have the scroll methods that the component had. I solved this by instead using a list of s instead and artificially breaking up the text. Though this workaround was lackluster. Hope this helps anyone looking for workarounds or anyone trying to solve this issue. I would recommend adding a iOS-specific prop that allows the developer to specify which text object to use (probably phrased in a more abstract manner, like |
As a quick clarification, this would happen even if you stripped React Native away and just had a vanilla-iOS app built with Swift and UIKit display hundreds of lines of text in a UILabel. UILabels are not intended for large text, Apple's documentation suggests as much. The issue at play is just that React Native's categories for text components don't perfectly align with Apple's categories for text objects. |
@jackthias good clarification, it sounds like the action here should be to:
|
@jackthias - hi - I'm just curious what you mean by "You can get your RN project to use the UITextView object instead of the UILabel object by using instead of and setting the appropriate props to make it read-only (and not select-able, if that matters)" I went looking for a read only property on the |
As a workaround using <TextInput multiline editable={false}>
Some very long text
</TextInput> works fine. I've looked into it a bit and we don't seem to be using UILabel. We are using 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 @interface TestView : UIView
@end
@implementation TestView
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] setFill];
UIRectFill(CGRectMake(0, 0, 100, 100));
}
@end
...
// as soon as one of these is bigger than 5k the view no longer renders.
TestView *view = [[TestView alloc] initWithFrame:CGRectMake(0, 0, 5000, 6000)];
view.backgroundColor = [UIColor whiteColor];
[rootViewController.view addSubview:view]; This example would result in a fully black screen (even the white background doesn't render). Removing 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 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 Edit 2: Got a fully working solution using CATiledLayer. |
@janicduplessis It's awesome that you're working on this. I'd love to help but I'm afraid the iOS internal rendering is above my pay grade. If there's something else I can do, let me know.
The Also, it being tied to the height of the
|
@mikelovesrobots You can try #24387, it should fix the issue completely! |
It sure does. I left a comment over there with comparison screenshots. |
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 issue described [here](#19453). On an iOS simulator the bug happens at around 500 lines which is quite annoying. It can also happen on a real device but requires a lot more text. To be more specific the amount of text doesn't actually matter, it is the size of the UIView that we use to draw the text. When we use `[drawRect:]` the view creates a bitmap to send to the gpu to render, if that bitmap is too big it cannot render. To fix this we can use `CATiledLayer` which will split drawing into smaller parts, that gets executed when the content is about to be visible. This drawing is also async which means the text can seem to appear during scroll. See https://developer.apple.com/documentation/quartzcore/calayer?language=objc. `CATiledLayer` also adds some overhead that we don't want when rendering small amount of text. To fix this we can use either a regular `CALayer` or a `CATiledLayer` depending on the size of the view containing the text. I picked 1024 as the threshold which is about 1 screen and a half, and is still smaller than the height needed for the bug to occur when using a regular `CALayer` on a iOS simulator. Also found this which addresses the problem in a similar manner and took some inspiration from the code linked there GitHawkApp/StyledTextKit#14 (comment) Fixes #19453 ## Changelog [iOS] [Fixed] - Use CALayers to draw text, fixes rendering for long text Pull Request resolved: #24387 Test Plan: - Added the example I was using to verify the fix to RNTester. - Made sure all other examples are still rendering properly. - Tested text selection Reviewed By: shergin Differential Revision: D15918277 Pulled By: sammy-SC fbshipit-source-id: c45409a8413e6e3ad272be39ba527a4e8d349e28
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 issue described [here](facebook#19453). On an iOS simulator the bug happens at around 500 lines which is quite annoying. It can also happen on a real device but requires a lot more text. To be more specific the amount of text doesn't actually matter, it is the size of the UIView that we use to draw the text. When we use `[drawRect:]` the view creates a bitmap to send to the gpu to render, if that bitmap is too big it cannot render. To fix this we can use `CATiledLayer` which will split drawing into smaller parts, that gets executed when the content is about to be visible. This drawing is also async which means the text can seem to appear during scroll. See https://developer.apple.com/documentation/quartzcore/calayer?language=objc. `CATiledLayer` also adds some overhead that we don't want when rendering small amount of text. To fix this we can use either a regular `CALayer` or a `CATiledLayer` depending on the size of the view containing the text. I picked 1024 as the threshold which is about 1 screen and a half, and is still smaller than the height needed for the bug to occur when using a regular `CALayer` on a iOS simulator. Also found this which addresses the problem in a similar manner and took some inspiration from the code linked there GitHawkApp/StyledTextKit#14 (comment) Fixes facebook#19453 ## Changelog [iOS] [Fixed] - Use CALayers to draw text, fixes rendering for long text Pull Request resolved: facebook#24387 Test Plan: - Added the example I was using to verify the fix to RNTester. - Made sure all other examples are still rendering properly. - Tested text selection Reviewed By: shergin Differential Revision: D15918277 Pulled By: sammy-SC fbshipit-source-id: c45409a8413e6e3ad272be39ba527a4e8d349e28
@janicduplessis thank you for your solution.
|
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
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 aScrollView
. 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.
The text was updated successfully, but these errors were encountered: