使用 expo-symbols 在 Expo 应用中渲染跨平台系统符号图标【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo导读expo-symbols是 Expo 官方提供的一个符号图标库它让 React Native 与 Expo 应用能够直接访问各平台的原生符号库在 iOS/tvOS 上使用 Apple 的 SF Symbols在 Android 与 Web 上使用 Google 的 Material Symbols。读完本文你将掌握SymbolView组件的完整用法跨平台符号映射、字重weight、渲染类型type、动画animation等全部配置项并理解其在当前仓库中的底层实现原理。背景与定位在 Expo 生态中expo-symbols是少数直接对接系统级符号资源的模块。从当前仓库的 package.json 可以看到它的正式描述Provides access to the SF Symbols library on iOS, and Material Symbols on Android and web, for Expo and React Native apps.当前仓库中的版本为57.0.1Unpublished 变更记录显示其刚刚从 beta 升级为 stable支持android、ios、tvos、web以及expo-go五种运行平台见 docs/pages/versions/unversioned/sdk/symbols.mdx 中的platforms声明。整个模块的源码布局非常清晰packages/expo-symbolspackages/expo-symbols/ ├── ios/ # iOS/tvOS/macOS 原生实现Swift Expo Modules Core ├── src/ │ ├── android/ # Material Symbols 的字体与名称映射含 7 种字重子目录 │ ├── SymbolModule.ts # 原生模块桥接入口 │ ├── SymbolModule.types.ts # 全部公开 TypeScript 类型定义 │ ├── SymbolView.tsx # Android/Web 的字体实现fallback 渲染 │ ├── SymbolView.ios.tsx # iOS 的原生视图桥接 │ └── index.ts # 模块入口导出 SymbolView 与类型 ├── package.json └── expo-module.config.json安装官方文档README.md给出了两条安装路径。在托管managedExpo 项目中在托管 Expo 项目中直接按照最新稳定版 API 文档中的安装说明操作即可。核心命令是npx expo install expo-symbols在 bare React Native 项目中对于裸 React Native 项目需要先确保已经安装并配置好expo包然后执行npx expo install expo-symbolsiOS 端安装完成后还需要运行 CocoaPods 安装npx pod-installexpo-symbols通过npx pod-install将 ios/ExpoSymbols.podspec 注册到原生工程中。从 package.json 可见其 peerDependencies 为expo、expo-font、react、react-native因此安装时需保证这些依赖已就绪。基础用法渲染一个符号所有功能的入口都是SymbolView组件从 src/index.ts 可以看到模块导出import { SymbolView } from expo-symbols;跨平台渲染传递平台映射对象最推荐的用法是传入一个包含各平台符号名的对象让同一段代码在所有平台渲染出语义一致的图标docs/pages/versions/unversioned/sdk/symbols.mdximport { SymbolView } from expo-symbols; import { StyleSheet, View } from react-native; export default function App() { return ( View style{styles.container} SymbolView name{{ ios: info.circle, android: info, web: info }} tintColor#007AFF size{35} / SymbolView name{{ ios: pencil.tip.crop.circle.badge.plus, android: home_and_garden, web: home_and_garden, }} style{styles.symbol} / /View ); } const styles StyleSheet.create({ container: { flex: 1, backgroundColor: #fff, alignItems: center, justifyContent: center, }, symbol: { width: 35, height: 35, margin: 5, }, });其中name的类型定义位于 SymbolModule.types.tsname: SFSymbol | { ios?: SFSymbol; android?: AndroidSymbol; web?: AndroidSymbol };iOS 符号名在 Apple 的 SF Symbols App 中浏览Android/Web 符号名在 Google Material Symbols 图标库中浏览。iOS 专用渲染直接传字符串如果只传入一个字符串它会被当作SF Symbol 名称只在 iOS 上渲染。在 Android 和 Web 上不渲染任何内容除非提供fallback{/* iOS-only: pass an SF Symbol name directly */} SymbolView nameairpods.chargingcase style{styles.symbol} typehierarchical /; {/* Use fallback for platforms where the symbol is not defined */} SymbolView name{{}} fallback{Text?/Text} /;这里fallback在 SymbolModule.types.ts 中定义为React.ReactNode可以渲染任意占位内容。字重Weight配置iOS 端iOS 上直接传字重字符串即可。SymbolWeight类型SymbolModule.types.ts支持以下取值取值说明unspecified默认由系统决定ultraLight/thin/light细字重系列regular常规iOS 默认映射medium/semibold/bold中粗字重系列heavy/black特粗字重系列Android 与 Web 端Android/Web 的字重实现与 iOS 不同它不是简单的字符串而是一个字体对象需要从expo-symbols/androidWeights子路径导入docs/pages/versions/unversioned/sdk/symbols.mdximport bold from expo-symbols/androidWeights/bold; SymbolView name{{ ios: star.fill, android: star, web: star }} weight{{ ios: bold, android: bold }} tintColorgold size{35} /;可用的字重导入包括bold、semiBold、medium、regular、light、extraLight、thin。每个字重子包内部都封装了一个对应的 Material Symbols 字体资源。以 src/android/weights/bold/index.ts 为例import { MaterialSymbols_700Bold } from expo-google-fonts/material-symbols/700Bold; const weight: AndroidSymbolWeight { name: MaterialSymbols_700Bold, font: MaterialSymbols_700Bold, }; export default weight;AndroidSymbolWeight类型src/android/index.ts由name字体族名与font字体文件两个字段组成。子路径的导出在 package.json 中通过 exports 字段的./androidWeights/*声明。注意weight属性本身也支持跨平台对象形式{ ios: SymbolWeight; android: AndroidSymbolWeight }SymbolModule.types.ts上述示例正是这种写法。渲染类型SymbolType与配色SymbolTypeSymbolModule.types.ts决定符号的配色变体默认值为monochrome类型说明monochrome单色变体跟随tintColorhierarchical由单一颜色衍生的层次配色palette使用调色板多色方案配合colors属性multicolor使用符号自带的多色变体如果存在iOS 端类型通过 SymbolView.swift 中的getSymbolConfig()转换为UIImage.SymbolConfiguration应用。其中palette模式要求colors至少提供两个颜色才会生效case .palette: if palette.count 1 { config config.applying(UIImage.SymbolConfiguration(paletteColors: palette)) }JS 侧colors与tintColor在 SymbolView.ios.tsx 中通过processColor转为原生颜色值后传入const colors Array.isArray(props.colors) ? props.colors : props.colors ? [props.colors] : []; ... colors: colors.map((c) processColor(c)), tint: processColor(props.tintColor),tintColor的语义在不同类型下有差异在 iOS 实现中当symbolType ! .hierarchical时使用withTintColor染色而hierarchical模式则通过SymbolConfiguration(hierarchicalColor:)使用tint未提供时回退为.systemBlue作为层级色SymbolView.swift。尺寸与缩放sizesize控制符号渲染尺寸默认值为 24SymbolModule.types.ts。在 iOS 原生视图中size 通过 style 的宽高生效SymbolView.ios.tsx在 Android/Web 的字体实现中size 同时作用于fontSize与lineHeightSymbolView.tsx。resizeModeresizeMode决定图像如何缩放以适应容器默认scaleAspectFit仅 iOS 生效。ContentMode类型SymbolModule.types.ts支持 13 种取值scaleToFill | scaleAspectFit | scaleAspectFill | redraw | center | top | bottom | left | right | topLeft | topRight | bottomLeft | bottomRightiOS 侧通过 SymbolRecords.swift 中的SymbolContentMode.toContentMode()映射到UIView.ContentMode在 macOS 上由于NSImageView使用imageScaling对齐类取值会被折叠为.scaleNone注释中说明由imageAlignment另行处理定位。scalescaleSymbolScaledefault | unspecified | small | medium | large默认unspecified仅 iOS 生效对应UIImage.SymbolConfiguration的 scale 维度SymbolRecords.swift。macOS 上由于NSImage.SymbolScale没有.default/.unspecified等价物这两个取值会被映射为.medium。动画为符号添加动效支持的动画类型animationSpec属性SymbolModule.types.ts允许为符号添加 Apple 的 Symbol Effects 动效仅 iOS 17 / tvOS 17 / macOS 14 可用。其结构为字段类型说明effect{ type, wholeSymbol?, direction? }动画效果类型repeatingboolean是否循环播放repeatCountnumber重复次数speednumber播放速度秒数缩放variableAnimationSpec对象可变颜色层动画effect.type支持三种基础动画SymbolModule.types.ts类型效果额外参数bounce弹跳wholeSymbol整体弹跳、directionup/downpulse脉冲wholeSymbolscale缩放wholeSymbol、directionup/down可变颜色层动画Variable ColorvariableAnimationSpec用于通过逐层改变透明度来吸引注意力SymbolModule.types.ts字段说明reversing每次重复时反向nonReversing每次重复不反向cumulative逐层累积保持启用直到循环结束会取消 iterativeiterative逐层短暂启用后恢复hideInactiveLayers完全隐藏非活动层dimInactiveLayers降低非活动层不透明度这些效果是**叠加compounding**的每个设为true的项都会额外增加一层效果。iOS 侧在 SymbolRecords.swift 中按顺序组装VariableColorSymbolEffect。动画的底层实现动画逻辑由 SymbolView.swift 中的addSymbolEffects()实现repeating与repeatCount组合成SymbolEffectOptionsrepeatCount取绝对值speed控制播放速度效果对象本身通过 SymbolEffects.swift 中的BounceEffect/PulseEffect/ScaleEffect结构体封装最终调用UIImageView.addSymbolEffect(_:options:animated:)。JS 侧只需设置animationSpecSymbolView.ios.tsx会自动把animated置为trueSymbolView.ios.tsxSymbolView namebell.badge animationSpec{{ effect: { type: bounce, direction: up }, repeating: true, }} /平台实现差异剖析iOS原生UIImageView渲染iOS 通过 SymbolModule.swift 将SymbolView注册为 Expo 原生视图每个 prop 都映射为 Swift 属性name、type、scale、tintColor、animated、weight、colors、resizeMode、animationSpec并在OnViewDidUpdateProps中调用reloadSymbol()刷新。核心渲染流程在 SymbolView.swift用UIImage(systemName:)加载 SF Symbol应用preferredSymbolConfiguration再叠加 tint 与动画效果。macOS 支持在 56.0.0 版本加入见 CHANGELOG.md由于NSImage没有withTintColor和preferredSymbolConfigurationmacOS 分支采用配置烘焙进 NSImage contentTintColor染色的替代方案SymbolView.swift。Android 与 Web字体渲染Android/Web 并没有原生视图而是通过 SymbolView.tsx 用expo-font 动态加载 Material Symbols 字体渲染先用loadAsync加载对应字重的字体文件再通过androidSymbolToString()把符号名转换为字体码位src/android/index.ts 使用String.fromCharCode从symbols.json映射表查询最终用带字体的Text渲染。默认符号颜色在 Android 上为系统主色system_primary_darkSymbolView.tsx。weight为对象时Android 侧字重通过 utils.ts 中的getFont()提取weight.android对应的字体对象而 iOS 侧 utils.ios.ts 返回null注释说明这是为了改善 tree-shaking。面向 ImageSource 的工具函数对于需要ImageSourcePropType而非组件的场景例如 tab bar 图标模块还导出了unstable_getMaterialSymbolSourceAsyncsrc/materialImageSource.ts它通过expo-font的renderToImageAsync把符号码位渲染成图片源import { unstable_getMaterialSymbolSourceAsync } from expo-symbols; const source await unstable_getMaterialSymbolSourceAsync(home, 24, #000000);该 API 标注为unstable仅 Android 平台可用且依赖expo-font提供renderToImageAsync。常见问题与注意事项字符串name仅 iOS 生效Android/Web 上不会渲染任何内容务必提供fallback或改用平台映射对象。palette类型需要至少 2 个颜色colors少于 2 个时 palette 配色不会生效SymbolView.swift。动画受系统版本限制Symbol Effects 需要 iOS 17 / tvOS 17 / macOS 14低版本上动画会被静默跳过removeAllSymbolEffects与addSymbolEffects都在 availability 守卫内见 SymbolView.swift。macOS 的 scale/weight 存在语义差异unspecified等取值会被折叠为medium/regularSymbolRecords.swift。Android 字重必须从子路径导入直接传字符串字重到 Android 侧无效需使用expo-symbols/androidWeights/{weight}提供的字体对象。style会被与size合并非原生 fallback 场景下style会与{ width: size, height: size }合并应用该行为在 Unpublished 变更记录中修复见 CHANGELOG.md。总结expo-symbols用一个SymbolView组件统一了 iOS 的 SF Symbols 与 Android/Web 的 Material Symbols 两套系统图标体系开发者只需维护一份平台映射即可实现跨平台一致的图标体验。配合字重、渲染类型、调色板配色与 iOS 17 的 Symbol Effects 动画它既能满足基础图标展示也能胜任 tab bar、按钮动效等进阶场景。对 iOS 原生实现ios/SymbolView.swift与 Android 字体渲染src/SymbolView.tsx的源码阅读可以帮助你更精确地掌握每个属性在不同平台上的真实行为边界。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考