规矩我懂:【 github.com/zwcshy/iOSK… 】
基于 KaTeX 的 iOS 公式渲染库,使用 Swift 实现,最低支持 iOS 12。
本工程将 KaTeX 前端库的 JS、CSS、字体等资源打包进 App Bundle,通过 WKWebView 在本地离线渲染 LaTeX 公式,API 风格与前端 katex.render() 对齐。
目录
整体设计
架构思路
KaTeX 本身是 JavaScript 数学排版引擎,完整移植其 TypeScript 解析器到 Swift 成本极高。本库采用 Hybrid 方案:
Swift 调用层(KatexView / KatexRenderer)
↓
WKWebView 加载本地 HTML 模板
↓
katex.min.js 解析 LaTeX 并排版
↓
katex.min.css + 字体文件 渲染视觉结果
↓
JavaScript 回传内容尺寸 → Swift 更新视图大小
数据流
sequenceDiagram
participant App as Swift App
participant View as KatexView
participant Renderer as KatexRenderer
participant WebView as WKWebView
participant KaTeX as katex.min.js
App->>View: setLatex("E=mc^2", options)
View->>Renderer: render(latex, options, webView)
Renderer->>WebView: evaluateJavaScript("renderMath(...)")
WebView->>KaTeX: katex.render(latex, container, options)
KaTeX-->>WebView: DOM + 尺寸
WebView-->>Renderer: { width, height }
Renderer-->>View: KatexRenderSize
View->>View: 更新 width/height 约束
与 KaTeX 前端的关系
| 能力 | KaTeX 前端 | IOSKatex |
|---|---|---|
| 数学公式 | ✅ 原生支持 | ✅ 一致 |
化学式文本 \ce{} | ✅ mhchem 扩展 | ✅ 已集成 mhchem.min.js |
| 苯环结构图 | ❌ 不支持 | ✅ 自定义 SVG + \includegraphics |
| 离线渲染 | 需自行打包资源 | ✅ 内置 Bundle |
工程结构
IOSKatex/
├── IOSKatex/ # 主 App Target
│ ├── Library/ # 📦 公式库核心(可复用)
│ │ ├── KatexView.swift # UIView 组件,界面展示公式
│ │ ├── KatexRenderer.swift # 渲染引擎,WKWebView + JS 桥接
│ │ ├── KatexOptions.swift # 渲染选项、化学模式枚举
│ │ ├── KatexBundle.swift # Bundle 资源路径
│ │ └── KatexError.swift # 错误类型
│ ├── KaTeX/ # 📁 KaTeX 静态资源(Copy Bundle Resources)
│ │ ├── katex.min.js # KaTeX 主引擎
│ │ ├── katex.min.css # 样式(字体路径已修正)
│ │ ├── katex-template.html # WebView 加载的 HTML 模板
│ │ ├── mhchem.min.js # 化学扩展(\ce{}、\pu{})
│ │ ├── auto-render.min.js # 自动渲染扩展(可选)
│ │ ├── fonts/ # KaTeX 字体(60 个文件)
│ │ └── *.svg # 自定义苯环结构图(非 KaTeX 自带)
│ ├── ViewController.swift # Demo 示例
│ ├── AppDelegate.swift # iOS 12 兼容启动
│ └── SceneDelegate.swift # iOS 13+ 场景管理
├── Scripts/
│ ├── copy-katex-assets.sh # 从 npm / 本地 dist 复制 KaTeX 资源
│ └── embed-katex-bundle.sh # 构建脚本参考(可选)
├── KaTeX-main/ # KaTeX 前端源码(参考,不参与编译)
├── IOSKatex.xcodeproj/
├── IOSKatexTests/
└── README.md
模块职责
| 模块 | 职责 |
|---|---|
KatexView | 面向 UI 的公式视图,自动管理 WebView 生命周期与尺寸 |
KatexRenderer | 单例 + 独立 WebView 渲染,提供 renderToString 等无 UI 能力 |
KatexOptions | 渲染参数,含化学模式选择 |
KatexBundle | 定位 Bundle 内 KaTeX 资源 |
katex-template.html | 注入 renderMath / remeasureMathSync 等 JS 函数 |
快速开始
环境要求
- Xcode 15+
- iOS 12.0+
- Swift 5
运行 Demo
- 打开
IOSKatex.xcodeproj - 选择模拟器或真机
- Run(
⌘R)
Demo 包含数学、物理、化学(mhchem 文本)、芳香烃(SVG 结构式)等示例。
最简示例
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let katexView = KatexView()
katexView.setLatex("E = mc^2")
katexView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(katexView)
NSLayoutConstraint.activate([
katexView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
katexView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
KatexView会根据渲染结果自动设置intrinsicContentSize,无需手动指定宽高。
核心 API
KatexView — 界面展示
let view = KatexView()
// 设置公式
view.setLatex("\\frac{a}{b}", options: KatexOptions(displayMode: true))
// 渲染完成回调
view.onRenderComplete = { result in
switch result {
case .success(let size):
print("渲染尺寸: \(size.width) x \(size.height)")
case .failure(let error):
print("渲染失败: \(error.localizedDescription)")
}
}
// 动态更新
view.latex = "\\sum_{i=1}^{n} i"
view.options.displayMode = true
KatexRenderer — 无 UI 渲染
// 渲染为 HTML 字符串
KatexRenderer.shared.renderToString("\\alpha + \\beta") { result in
switch result {
case .success(let html):
print(html) // 可用于 NSAttributedString / 富文本
case .failure(let error):
print(error)
}
}
KatexOptions — 渲染配置
var options = KatexOptions()
options.displayMode = true // 块级公式
options.throwOnError = false // 出错时显示红色原文
options.macros = ["\\R": "\\mathbb{R}"]
化学公式两种模式
化学内容有两种渲染方式,通过 KatexChemistryMode 选择:
模式 1:mhchem 文本(与 KaTeX 前端一致)
使用 \ce{} 命令,渲染为带上下标、箭头、化学键的文字公式。
katexView.setLatex(
"\\ce{C6H6 + HNO3 -> C6H5-NO2 + H2O}",
options: .chemistryText(displayMode: true)
)
| 属性 | 值 |
|---|---|
| 枚举 | .mhchemText |
| 预设 | KatexOptions.chemistryText() |
| LaTeX | \ce{H2O}、\ce{2H2 + O2 -> 2H2O} |
| 依赖 | mhchem.min.js(已内置) |
| 苯环 | ❌ 无结构图,仅文字 |
模式 2:SVG 结构式(iOS 扩展)
使用 \includegraphics 加载本地 SVG 苯环图,适合需要可视化结构的场景。
katexView.setLatex(
"\\includegraphics[height=2em]{benzene.svg}\\quad \\mathrm{C_6H_6}",
options: .chemistryStructure()
)
| 属性 | 值 |
|---|---|
| 枚举 | .structureSVG |
| 预设 | KatexOptions.chemistryStructure() |
| LaTeX | \includegraphics[height=2em]{aniline.svg} |
| 依赖 | 本地 SVG 文件 + trust: true(自动开启) |
| 苯环 | ✅ 六边形结构图 |
模式对比
| mhchem 文本 | SVG 结构式 | |
|---|---|---|
| 与前端一致 | ✅ | ❌(iOS 扩展) |
| 苯环结构图 | ❌ | ✅ |
| 复杂反应式 | ✅ 简洁 | 需组合多个 SVG |
| 离线资源 | 仅 JS | JS + SVG 文件 |
| 推荐场景 | 一般化学式、反应方程式 | 芳香烃结构、教学图示 |
手动指定模式
var options = KatexOptions()
options.chemistryMode = .mhchemText // 或 .structureSVG / .none
options.displayMode = true
katexView.setLatex(latex, options: options)
渲染选项
KatexOptions 与 KaTeX Options 文档 对齐:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
displayMode | Bool | false | 块级(true)/ 行内(false) |
chemistryMode | KatexChemistryMode | .none | 化学渲染模式 |
throwOnError | Bool | true | 不支持命令时是否抛错 |
errorColor | String | "#cc0000" | 错误显示颜色 |
trust | Bool | false | 启用 \includegraphics(结构式模式自动开启) |
macros | [String: String] | [:] | 自定义宏 |
output | KatexOutput | .htmlAndMathml | 输出格式 |
资源管理
更新 KaTeX 资源
KaTeX 核心资源来自 npm 包,通过脚本复制到工程:
# 从 npm 下载 KaTeX 0.17.0(推荐)
./Scripts/copy-katex-assets.sh
# 从本地 KaTeX-main/dist 复制(需先 build)
cd KaTeX-main && pnpm install && pnpm build
./Scripts/copy-katex-assets.sh --local
脚本会复制:
katex.min.js/katex.min.cssmhchem.min.jsfonts/目录下全部字体- 自动修正 CSS 中
fonts/路径(适配 Xcode 扁平化打包)
注意:
*.svg苯环结构图为工程自定义资源,不会被脚本覆盖。
Bundle 打包说明
Xcode 使用 PBXFileSystemSynchronizedRootGroup 同步 IOSKatex/ 目录,KaTeX/ 下文件会被扁平化复制到 App Bundle 根目录。因此:
- CSS 中字体路径已从
url(fonts/xxx)修正为url(xxx) \includegraphics{benzene.svg}直接使用 Bundle 根目录文件名KatexBundle.resourceDirectoryURL返回Bundle.main.bundleURL
添加自定义 SVG 结构图
- 将 SVG 放入
IOSKatex/KaTeX/ - 确保 SVG 为合法 XML(下标用
<tspan baseline-shift="sub">而非 Unicode 下标字符) - 在 LaTeX 中引用:
\includegraphics[height=2em, alt=描述]{your-structure.svg}
- 使用
.chemistryStructure()或chemistryMode = .structureSVG
集成到其他工程
当前公式库代码位于 IOSKatex/Library/,资源位于 IOSKatex/KaTeX/。集成步骤:
-
复制文件
IOSKatex/Library/*.swift→ 目标工程IOSKatex/KaTeX/整个目录 → 目标工程
-
加入 Target
- Swift 文件加入 Compile Sources
KaTeX/下所有资源加入 Copy Bundle Resources
-
依赖框架
UIKitWebKit
-
iOS 12 兼容
- 若目标 App 使用 SceneDelegate,需确保 iOS 12 有 Storyboard / AppDelegate 回退路径
- 避免在 Library 代码中使用 iOS 13+ 专属 API
-
使用
let formulaView = KatexView()
formulaView.setLatex("\\int_0^\\infty e^{-x^2} dx", options: KatexOptions(displayMode: true))
后续可进一步抽取为独立 Framework / Swift Package。
已知限制
- 渲染依赖 WKWebView:每个
KatexView持有一个 WebView,大量公式同时展示时注意内存。 - 不支持 chemfig / tikz:完整 LaTeX 化学结构绘制不在 KaTeX 能力范围内,结构式需用 SVG 方案。
- 图片异步加载:含
\includegraphics的公式会在渲染后多次重新测量尺寸(50ms / 150ms / 350ms)。 - 宽公式横向滚动:超长公式会撑开容器,外层需自行提供
UIScrollView。 - trust 安全:
.structureSVG模式开启trust,仅加载可信任的本地 SVG,勿传入不可信 LaTeX。
常见问题
公式被截断 / 宽度不对
- 确保外层容器未限制
KatexView的 trailing 宽度 - 含 SVG 的公式需等待图片加载完成后的二次测量
- 参考 Demo 中
card.trailing = katexView.trailing + 12的布局方式
SVG 显示裂图
- 检查 SVG 是否为合法 XML(避免损坏的 Unicode 下标)
- 确认 SVG 已加入 Copy Bundle Resources
- 确认使用了
.chemistryStructure()或trust: true
\ce{} 不生效
- 确认
mhchem.min.js在 Bundle 中 - 确认
katex-template.html已加载<script src="mhchem.min.js"> - 使用
.chemistryText()预设
JSONSerialization 崩溃
- LaTeX 字符串通过
[latex]包装序列化,已在KatexRenderer.jsonString(from:)中处理,无需调用方关心
如何升级 KaTeX 版本
- 修改
Scripts/copy-katex-assets.sh中的KATEX_VERSION - 修改
KatexBundle.version - 运行
./Scripts/copy-katex-assets.sh - 重新编译验证 Demo
许可证
- KaTeX 核心:MIT License
- mhchem 扩展:见 KaTeX contrib/mhchem
- 本工程 SVG 结构图与 Swift 封装:随项目分发