Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React native determine when text is cut off

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?

like image 365
Patrick Klitzke Avatar asked May 01 '16 01:05

Patrick Klitzke


3 Answers

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.

like image 75
Mike Avatar answered Nov 20 '22 11:11

Mike


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.

like image 28
Kashish Grover Avatar answered Nov 20 '22 12:11

Kashish Grover


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>
like image 6
George Avatar answered Nov 20 '22 13:11

George