是否可以在 React Native 中使用 iOS 13 种系统字体?

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

我想在我的 React Native 应用程序中使用自 iOS 13 以来可用的新系统字体。特别是,我需要 roundedserifmonospaced。在本机方面,它是使用 UIFontDescriptor.SystemDesign https://developer.apple.com/videos/play/wwdc2019/227/?time=142

完成的

在 React Native 方面,我们可以将 { fontFamily: 'System' }StyleSheet 结合使用,但它仅提供默认的无衬线设计。

是否可以使用一些 Swift 代码来修改文本组件的字体描述符?或者我应该在 React Native 存储库中提出一个问题并提出功能请求吗?

您不能在 React Native 中指定系统字体的字体变体。您可以下载并使用该字体作为自定义字体。 [1]

我建议从 utils/primitives 文件中导出组件并设置该组件的样式以使用自定义字体。

// Text.js
import {Text} from 'react-native'
export const Text = () => <Text style={{fontFamily: 'customFontFamily'}} />;



// App.js
import {View} from 'react-native'
import {Text} from './Text'

const App = () => {
return (
  <View>
    <Text>Hello World</Text>
  </View>
 )
}'

export default App;

[1] https://gist.github.com/parshap/cf9cf0388d55a044004e5e78fa317b39

React Native 不使用 UIFontDescriptor 和 iOS 上的指定设计,但您可以使用下一个运行时黑客技术 - “Method Swizzling”来拦截对 UIFont 的系统调用和 return 一个需要的但它应该在 ObjC 中实现。

只需将此 ObjC 代码添加到您的 XCode 项目中:

// 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

然后您可以在 React Native 文件中使用 'System-Rounded'、'System-Serif' 和 'System-Monospaced' 字体:

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,
  },
});