I am using react-native with a limited number of lines, that are shown as ...
using
<Text numberOfLines={4}> {myText} </Text>
Now my issue is, if the text is cut off I would like to show it some special image, to navigate to a new view. I am wondering whether there is a property i can use to test if the text is being cut off?
There isn't a property for this right now (unfortunately).
There's a feature request here: https://github.com/facebook/react-native/issues/2496 (also some suggestions of how you could get it working, but the implementation links are down).
You could measure the space occupied by a certain number of lines, then handle it yourself? But not ideal.
I answered a related question here -- https://stackoverflow.com/a/60500348/5449850
This has been a requirement in almost every React Native app that I have worked on so far. I finally have a solution, and I have open sourced it.
https://github.com/kashishgrover/react-native-see-more-inline
https://www.npmjs.com/package/react-native-see-more-inline
As I mentioned in the repo,
My motivation of building this was that I couldn't find any library/implementation that would place the "see more" link inline with the text. All the other implementations I found would place the link under the text. This package uses text width, and using a simple binary search it (almost) accurately calculates where it should place the "see more" link.
I am not sure if this is the most efficient solution out there, but it solves my use case. I will update the answer if and when I find a better solution.
This is what I did:
findTruncationIndex = async (containerWidth) => {
if (
this.containerWidthToTruncationIndexMap
&& this.containerWidthToTruncationIndexMap[containerWidth]
) {
this.setState({ truncationIndex: this.containerWidthToTruncationIndexMap[containerWidth] });
return;
}
const {
children: text,
style: { fontSize, fontFamily, fontWeight },
seeMoreText,
numberOfLines,
} = this.props;
const { width: textWidth } = await reactNativeTextSize.measure({
text,
fontSize,
fontFamily,
fontWeight,
});
const textWidthLimit = containerWidth * numberOfLines;
if (textWidth < textWidthLimit) {
this.setState({ truncationIndex: undefined });
return;
}
const { width: seeMoreTextWidth } = await reactNativeTextSize.measure({
text: ` ...${seeMoreText}`,
fontSize,
fontFamily,
fontWeight,
});
const truncatedWidth = textWidthLimit - 2 * seeMoreTextWidth;
let index = 0;
let start = 0;
let end = text.length - 1;
while (start <= end) {
const middle = start + (end - start) / 2;
// eslint-disable-next-line no-await-in-loop
const { width: partialWidth } = await reactNativeTextSize.measure({
text: text.slice(0, middle),
fontSize,
fontFamily,
fontWeight,
});
if (Math.abs(truncatedWidth - partialWidth) <= 10) {
index = middle;
break;
} else if (partialWidth > truncatedWidth) {
end = middle - 1;
} else {
start = middle + 1;
}
}
const truncationIndex = Math.floor(index);
// Map truncation index to width so that we don't calculate it again
this.containerWidthToTruncationIndexMap = {
...this.containerWidthToTruncationIndexMap,
[containerWidth]: truncationIndex,
};
this.setState({ truncationIndex });
};
You can see the full implementation of this component in the GitHub link that I shared above.
There's now a new event called onTextLayout
which can give you the number of lines in your text. Once you fetch this number, you could check if it's greater than the max number of lines you want to have, and if that's the case you can set some state that adds a numberOfLines
prop and can be also used to determine if the text was truncated.
Here's an example of how you could get the number of lines:
<Text
onTextLayout={(e) => console.log("Number of lines: " + e.nativeEvent.lines.length) }
>
{someText}
</Text>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With