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

Props font-weight doesn't work on iOS by use expo-font and custom font #9149

Closed
Melekhin opened this issue Jul 9, 2020 · 5 comments
Closed
Labels

Comments

@Melekhin
Copy link

Melekhin commented Jul 9, 2020

Hello, guys!

Added custom font (sf pro text regular, sf pro display regular) to the project. The font is substituted correctly, but when I try to change the font weight of a given font, nothing happens on iOS. On Android, the font weight changes only if you pass the value bold. And if you transfer font weight with a number, nothing happens on the Android or on the iOS.

        // Load fonts
        await Font.loadAsync({
          ...Ionicons.font,
          'sfpro-display': require('@src/assets/fonts/SFProDisplay.ttf'),
          'sfpro-text': require('@src/assets/fonts/SFProText.ttf'),
        });

What is the way to set the font weight of text to make it work?

I use version modules:
"expo": "^38.0.0",
"expo-font": "~8.2.1",
"react": "16.11.0"

@byCedric
Copy link
Member

byCedric commented Jul 9, 2020

Hi @Melekhin! Unfortunately, React Native has certain limitations to custom fonts. On Android, as you mention, only the normal and bold font weights are supported. Because of this, the best approach is to declare them as a different font family. Here is an example on Stack Overflow and one in our docs, I think it can help you further.

Hope this helps!

@byCedric byCedric closed this as completed Jul 9, 2020
@Melekhin
Copy link
Author

@byCedric, Thank you very much for the quick response!
It’s a pity that the React Native does not yet allow you to adjust the weight for custom fonts.

The links turned out to be very useful, thank's!

@harismh
Copy link
Contributor

harismh commented Jul 14, 2021

FWIW, I ran into this same issue and did find some workarounds.

Web and Android appear to be unaffected by this iOS limitation. You can simply load all of the bold, italic, and bold-italic variants via Font.loadAsync(customFonts) in App.json. Then later in RN code, declaring something like style = {{fontWeight: "bold"}} just works. On iOS, however, in order for custom font variants to show up you have to explicitly set the fontFamily prop as the docs linked above has mentioned.

One hacky workaround I found that works cross-platform involves parsing the RN styles via Regex and selecting the fontFamily dynamically like so:

const CUSTOM_FONT_FAMILY = {
  regular: "SomeFont",
  bold: "SomeFont-Bold",
  italic: "SomeFont-Italic",
  boldItalic: "SomeFont-BoldItalic"
};
const BOLD_FONT_WEIGHT_REGEX = /"(font(?:-w|W)eight)":("(?:[789]00|bold)")/;
const ITALIC_FONT_STYLE_REGEX = /"(font(?:-s|S)tyle)":("italic")/;

const hasStyleProp = (tester: RegExp) => (style: ?TextStyleProp) =>
  style ? tester.test(JSON.stringify(style)) : false;
const hasBoldFontProp = hasStyleProp(BOLD_FONT_WEIGHT_REGEX);
const hasItalicFontProp = hasStyleProp(ITALIC_FONT_STYLE_REGEX);

const makeFontFamilyPropFix = (style: ?TextStyleProp) =>
  Platform.OS === "ios"
    ? {
        fontFamily:
          hasBoldFontProp(style) && hasItalicFontProp(style)
            ? CUSTOM_FONT_FAMILY.boldItalic
            : hasBoldFontProp(style)
            ? CUSTOM_FONT_FAMILY.bold
            : hasItalicFontProp(style)
            ? CUSTOM_FONT_FAMILY.italic
            : CUSTOM_FONT_FAMILY.regular
      }
    : {
        fontFamily: customFontFamily.regular
      };

// example use
const CustomFontText = ({ style, ...restProps }) => {
  return (
    <Text
      {...restProps}
      style={StyleSheet.flatten([style, makeFontPropFix(style)])}
    />
  );
};

Definitely not ideal, but with this fontWeight and fontStyle props work across iOS, Android, and Web, and Regex is pretty performant at parsing and searching, especially when dealing with nested styles.

@vbylen
Copy link

vbylen commented Sep 19, 2021

FWIW, I ran into this same issue and did find some workarounds.

Web and Android appear to be unaffected by this iOS limitation. You can simply load all of the bold, italic, and bold-italic variants via Font.loadAsync(customFonts) in App.json. Then later in RN code, declaring something like style = {{fontWeight: "bold"}} just works. On iOS, however, in order for custom font variants to show up you have to explicitly set the fontFamily prop as the docs linked above has mentioned.

One hacky workaround I found that works cross-platform involves parsing the RN styles via Regex and selecting the fontFamily dynamically like so:

const CUSTOM_FONT_FAMILY = {
  regular: "SomeFont",
  bold: "SomeFont-Bold",
  italic: "SomeFont-Italic",
  boldItalic: "SomeFont-BoldItalic"
};
const BOLD_FONT_WEIGHT_REGEX = /"(font(?:-w|W)eight)":("(?:[789]00|bold)")/;
const ITALIC_FONT_STYLE_REGEX = /"(font(?:-s|S)tyle)":("italic")/;

const hasStyleProp = (tester: RegExp) => (style: ?TextStyleProp) =>
  style ? tester.test(JSON.stringify(style)) : false;
const hasBoldFontProp = hasStyleProp(BOLD_FONT_WEIGHT_REGEX);
const hasItalicFontProp = hasStyleProp(ITALIC_FONT_STYLE_REGEX);

const makeFontFamilyPropFix = (style: ?TextStyleProp) =>
  Platform.OS === "ios"
    ? {
        fontFamily:
          hasBoldFontProp(style) && hasItalicFontProp(style)
            ? CUSTOM_FONT_FAMILY.boldItalic
            : hasBoldFontProp(style)
            ? CUSTOM_FONT_FAMILY.bold
            : hasItalicFontProp(style)
            ? CUSTOM_FONT_FAMILY.italic
            : CUSTOM_FONT_FAMILY.regular
      }
    : {
        fontFamily: customFontFamily.regular
      };

// example use
const CustomFontText = ({ style, ...restProps }) => {
  return (
    <Text
      {...restProps}
      style={StyleSheet.flatten([style, makeFontPropFix(style)])}
    />
  );
};

Definitely not ideal, but with this fontWeight and fontStyle props work across iOS, Android, and Web, and Regex is pretty performant at parsing and searching, especially when dealing with nested styles.

Thank you. Would I have to change anything to this code snippet if I wanted to include medium and semibold styles?

@harismh
Copy link
Contributor

harismh commented Sep 19, 2021

Thank you. Would I have to change anything to this code snippet if I wanted to include medium and semibold styles?

@10000multiplier Some things would need to be changed, but this solution should be able to accommodate further weights. Primarily, the CUSTOM_FONT_FAMILY map would need to expand to include the font names for the extra variants, and an additional Regex fn and check would need to be added given the bold regex used above is only checking for weights between 700-900. You could also refactor the bold font check into a switch statement with cases for each font weight.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 21, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants