Skip to content
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

adjustsFontSizeToFit not working #20906

Closed
3 tasks done
kyo504 opened this issue Aug 29, 2018 · 8 comments
Closed
3 tasks done

adjustsFontSizeToFit not working #20906

kyo504 opened this issue Aug 29, 2018 · 8 comments
Labels
Bug Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@kyo504
Copy link
Contributor

kyo504 commented Aug 29, 2018

Environment

  React Native Environment Info:
    System:
      OS: macOS High Sierra 10.13.6
      CPU: x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
      Memory: 184.31 MB / 16.00 GB
      Shell: 5.3 - /bin/zsh
    Binaries:
      Node: 8.11.3 - /usr/local/bin/node
      Yarn: 1.9.4 - /usr/local/bin/yarn
      npm: 5.6.0 - /usr/local/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 11.4, macOS 10.13, tvOS 11.4, watchOS 4.3
      Android SDK:
        Build Tools: 23.0.1, 26.0.1, 26.0.3, 27.0.3, 28.0.0
        API Levels: 23, 25, 26, 27, 28
    IDEs:
      Android Studio: 3.1 AI-173.4819257
      Xcode: 9.4.1/9F2000 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.4.1 => 16.4.1
      react-native: 0.56.0 => 0.56.0
    npmGlobalPackages:
      react-native-cli: 2.0.1

Description

Since 0.54.x, adjustsFontSizeToFit option in Text has not been working. It worked as expected on 0.53.x

Reproducible Demo

import React, {Component} from 'react';
import {StyleSheet, Text, View} from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text} allowFontScaling>Lorem Ipsum is simply dummy text of the printing and typesetting industry.</Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    backgroundColor: '#F5FCFF',
  },
  text: {
    fontSize: 30,
    color: '#333333',
    marginBottom: 5,
  },
});
@pabloga265
Copy link

pabloga265 commented Oct 22, 2018

This is still happening in 0.56 and 0.57, adjustsFontSizeToFit works fine on iOS but does nothing on Android

@otaviogaiao
Copy link

Yep, still happening on 0.57. Is there any workaround?

@pabloga265
Copy link

pabloga265 commented Nov 7, 2018

Extremely hacky and extremely no-no. Full combat solution until I find something better

I created a text component that for Ios basically returns Text and in android returns a Text with this onLayout thing on top of it.

Please I can't encourage anyone enough to not use this in their projects, and find a real solution but if you are in a hurry this patched my problem

<Text
  onLayout={(event) => {
    if (!this.props.adjustsFontSizeToFit || typeof this.props.children !== 'string') return
    let {width} = event.nativeEvent.layout
    this.setState({fontSize: Math.floor(1.7 * (width / this.props.children.length))})
  }}
  {...this.props}
  style={[this.props.style, this.state.fontSize && {fontSize: this.state.fontSize}]}
>
  {this.props.children}
</Text>)

This is not a fix of course 1.7 is a random number that seems to fit my project properly in all resolutions I have tested so far, onLayout fires after render so there is a bit of a visual glitch etc... etc...

I will try to find a decent fix if it doesn't get fixed eventually but for now, I'm going with this shameful hack.

@AshtonKem
Copy link

I can confirm that this is still happening.

@pabloga265 I think the above might work better if you can use Dimensions to determine screen width and calculate the correct fontSize before the first render.

@jklingen92
Copy link

@pabloga265 I took your code and took out the constants. TLDR: you're code breaks for some font-families, mine should fix that. See below.

Basically, I took figured if we have to render the text twice, we might as well get some useful information, so I'm rendering the first pass at fontSize 5 (if we start out too small it gets less accurate because the letters are so small). I did some testing and found the following:
monospace width = length * fontSize * 3 / 5
where length is the number of characters in the text. We can also get the width at fontSize 5:
monospace width at fontSize 5 = length * 5 * 3 / 5 = length * 3
Now, we can calculate how wide our actual text would be in myFontFamily after render by applying the ratio between these two monospace widths to the reported width at fontSize 5 from onLayout:

myFontFamily width = myFontFamily width at 5 (reported width ) * ( monospace width / monospace width at 5)
myFontFamily width = reported width * length * fontSize * 3 / (length * 3 * 5)
myFontFamily width = reported width * fontSize / 5

We can use also use this equation to calculate an appropriate fontSize to get the desired width of the text element.

I was also running into some problems with multiple adjustments so I created a switch that would only allow it to be adjusted one time.

I also made sure to use the smaller of the two fontSize values, as your code adjusts no matter what, while the adjustsFontSizeToFit property only shrinks text that is clipping.

export default class ResponsiveText extends React.Component {
  /*
    This is an extension of text that handles implements the adjustsFontSizeToFit for android. The bug that it's
    patching is documented here:

    https://github.com/facebook/react-native/issues/20906

   */
  state = {
    _style: {
      fontSize: 5,
      width: null,
    },
    _adjusted: false
  }

  onLayout = (event) => {
    const { adjustsFontSizeToFit, style, children } = this.props
    if (adjustsFontSizeToFit && style.width && typeof children === 'string') {
      if (!this.state._adjusted) {
        const {width} = event.nativeEvent.layout
        this.setState({
          _style: {
            fontSize: Math.min(style.fontSize, Math.floor(5 * style.width / width)),
            width: style.width,
          },
          _adjusted: true,
        })
      }
    }
    else {
      this.setState({
        _style: {
          fontSize: this.props.style.fontSize,
          width: this.props.style.width,
        },
        _adjusted: true,
      })
    }
  }

  render() {
    return (
      Platform.OS === 'ios'
        ? <Text {...this.props}/>
        : <Text onLayout={this.onLayout} {...this.props}
                style={[this.props.style, this.state._style]}
          />
    )
  }
}

@leonidus96
Copy link

leonidus96 commented Apr 21, 2019

If you want to use a value of "numberOfLines" >= 1 you have to modify a little this component, because how it's written it will always calculate a fontSize that will fit in just 1 line. I needed a numberOfLines = 2.

changing

const { adjustsFontSizeToFit, style, children } = this.props

in

const { adjustsFontSizeToFit, style, children,numberOfLines } = this.props

and

fontSize: Math.min(style.fontSize, Math.floor(5 * style.width / width))

in

fontSize: Math.min(style.fontSize, Math.floor(5 * style.width / width * numberOfLines))

did the trick for me.

@stale
Copy link

stale bot commented Aug 4, 2019

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 a "Discussion" or add it to the "Backlog" and I will leave it open. Thank you for your contributions.

@stale stale bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 4, 2019
@stale
Copy link

stale bot commented Aug 11, 2019

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please feel free to create a new issue with up-to-date information.

@stale stale bot closed this as completed Aug 11, 2019
@facebook facebook locked as resolved and limited conversation to collaborators Aug 12, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests

8 participants