Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use iOS 13 system fonts in React Native?

I'd like to use new system fonts available since iOS 13 in my React Native app. In particular, I need rounded, serif and monospaced ones. On the native side it is done using UIFontDescriptor.SystemDesign https://developer.apple.com/videos/play/wwdc2019/227/?time=142

On React Native side we can use { fontFamily: 'System' } with StyleSheet but it only gives the default sans-serif design.

Is it possible to use some Swift code to modify the font descriptor of a Text component? Or should I file an issue in React Native repo with a feature request?

like image 834
Ash Hissorrow Avatar asked Sep 15 '25 13:09

Ash Hissorrow


1 Answers

React Native doesn't use UIFontDescriptor with the specified design on iOS but you can use next runtime hackery technique - "Method Swizzling" to intercept the systems call to UIFont and return a needed one but it should be implemented in ObjC.

Just add this ObjC code to your XCode project:

// UIFont+SystemDesign.m

#import <objc/runtime.h>

@implementation UIFont (SystemDesign)

+ (void)load {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    Class class = object_getClass((id)self);
    
    SEL originalSelector = @selector(fontWithName:size:);
    Method originalMethod = class_getClassMethod(class, originalSelector);
    
    SEL swizzledSelector = @selector(_fontWithName:size:);
    Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
    
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
      class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    else {
      method_exchangeImplementations(originalMethod, swizzledMethod);
    }
  });
}

#pragma mark - Method Swizzling

+ (UIFont *)_fontWithName:(NSString *)fontName size:(CGFloat)fontSize {
  NSString* const systemRounded = @"System-Rounded";
  NSString* const systemSerif = @"System-Serif";
  NSString* const systemMonospaced = @"System-Monospaced";
  
  NSArray* fonts = @[systemRounded, systemSerif, systemMonospaced];
  
  if ([fonts containsObject:fontName]) {
    if (@available(iOS 13.0, *)) {
      NSDictionary* designs = @{systemRounded : UIFontDescriptorSystemDesignRounded,
                              systemSerif : UIFontDescriptorSystemDesignSerif,
                              systemMonospaced : UIFontDescriptorSystemDesignMonospaced};
      
      UIFontDescriptor *fontDescriptor = [UIFont systemFontOfSize:fontSize].fontDescriptor;
      fontDescriptor = [fontDescriptor fontDescriptorWithDesign: designs[fontName]];
      return [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
    }
    else {
      return [UIFont systemFontOfSize:fontSize];
    }
  }
  
  return [self _fontWithName:fontName size:fontSize];
}

@end

Then you can use 'System-Rounded', 'System-Serif' and 'System-Monospaced' fonts into your React Native files:

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.systemDefault}>Default</Text>
      <Text style={styles.systemRounded}>Rounded</Text>
      <Text style={styles.systemSerif}>Serif</Text>
      <Text style={styles.systemMonospaced}>Monospaced</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  systemDefault: {
    fontFamily: 'System',
    fontSize: 20,
  },
  systemRounded: {
    fontFamily: 'System-Rounded',
    fontSize: 20,
  },
  systemSerif: {
    fontFamily: 'System-Serif',
    fontSize: 20,
  },
  systemMonospaced: {
    fontFamily: 'System-Monospaced',
    fontSize: 20,
  },
});
like image 196
iUrii Avatar answered Sep 17 '25 03:09

iUrii