MarkdownUI 是目前 SwiftUI 生态中最完善的 Markdown 渲染库,支持完整的 CommonMark 语法、代码高亮和深度样式定制。本文介绍从安装到自定义主题的完整用法。
安装
在Xcode中,找到File → Add Package Dependencies,输入:
https://github.com/gonzalezreal/swift-markdown-ui
添加完成后,在需要使用的文件顶部导入:
import MarkdownUI
基本用法
Markdown 视图接受一个字符串,直接渲染为格式化内容:
import SwiftUI
import MarkdownUI
struct MarkdownView: View {
let markdownContent = """
# 欢迎学习 Swift
这是一个 **粗体** 和 *斜体* 的例子。
## 代码示例
```swift
let greeting = "Hello, Swift!"
print(greeting)
```
"""
var body: some View {
ScrollView {
Markdown(markdownContent)
.padding()
}
}
}
显示样式:

主题规则
1、内置主题
MarkdownUI 提供三个内置主题,通过 .markdownTheme() 修饰符应用:
Markdown(content)
.markdownTheme(.gitHub) // GitHub 风格,最常用
.markdownTheme(.docC) // Apple 文档风格
.markdownTheme(.basic) // 最简风格

2、自定义主题
通过扩展 Theme 定义自己的主题,然后用 .markdownTheme(.custom) 应用:
Markdown(content)
.markdownTheme(.custom) // 使用自定义主题
extension Theme {
static var custom: Theme {
Theme()
.text {
FontSize(16)
FontWeight(.light)
}
}
}
如果需要重写现有的主题,也可以直接继承:
extension Theme {
static var custom: Theme {
Theme.gitHub
.text {
FontSize(16)
FontWeight(.light)
}
}
}
推荐从 Theme.gitHub 而不是 Theme() 开始继承。Theme() 是空白主题,行内代码高亮、链接颜色等样式都需要手动补齐;Theme.gitHub 已经处理好了这些细节,只需覆盖想改的部分。
Theme 块类型参考
自定义 Theme 时可以覆盖以下块类型:
Theme()
// 块级元素
.paragraph { } // 普通正文段落
.heading1 { } // # 一级标题
.heading2 { } // ## 二级标题
.heading3 { } // ### 三级标题
.heading4 { }
.heading5 { }
.heading6 { }
.blockquote { } // > 引用块
.codeBlock { } // ``` 代码块
.image { } //  图片
.listItem { } // - 列表项
.table { } // 表格
.tableCell { } // 表格单元格
.thematicBreak { } // --- 分隔线
// 内联元素(在 markdownTextStyle 闭包中使用)
.text { } // 普通文字
.inlineCode { } // `code` 行内代码
.link { } // [文字](url) 链接
.strong { } // **粗体**
.emphasis { } // *斜体*
.strikethrough { } // ~~删除线~~
块级元素的闭包中可以使用任意 SwiftUI 修饰符;内联元素使用 MarkdownUI 专属的文字样式 DSL,可用属性包括 FontSize、FontWeight、FontFamily、ForegroundColor、BackgroundColor 等。
configuration 是什么
在自定义块级元素样式时,闭包会传入一个 configuration 参数,它包含两部分内容。
configuration.label 是该块渲染后的 SwiftUI View,可以在它上面叠加任意 SwiftUI 修饰符:
.heading1 { configuration in
configuration.label // 这是标题内容对应的 SwiftUI View
.padding(.bottom, 4) // 可以直接加 SwiftUI 修饰符
.background(Color.gray.opacity(0.1))
}
configuration.content 是该块的原始 Markdown 内容,可以用来读取文字、做条件判断等。此外不同块类型还携带各自的附加信息:
.codeBlock { configuration in
VStack(alignment: .leading) {
if let language = configuration.language {
Text(language) // 读取代码块的语言标注,例如 "swift"
.font(.caption)
}
configuration.label
}
}
markdownTextStyle 是什么
markdownTextStyle 是 MarkdownUI 提供的修饰符,专门用于设置文字的 AttributedString 属性。它只能在 configuration.label 上调用,或在内联元素(.text、.inlineCode 等)的闭包中直接使用。
.heading1 { configuration in
configuration.label
.markdownTextStyle { // 在这个闭包里设置文字属性
FontSize(24)
FontWeight(.semibold)
}
}
它和 SwiftUI 的 .font() 修饰符的核心区别在于作用范围:markdownTextStyle可以保留Markdown 内联样式,SwiftUI 的 .font()可能会覆盖Markdown 内联样式,导致整体样式统一。
因此在 markdownTextStyle 闭包里不能使用 SwiftUI 的 .font()、.foregroundColor() 等修饰符,只能使用 MarkdownUI 自己的 DSL。如果需要用 SwiftUI 修饰符,直接加在 configuration.label 上即可:
.heading1 { configuration in
configuration.label
.markdownTextStyle {
FontSize(24) // MarkdownUI DSL,写在闭包里
}
.padding(.bottom, 4) // SwiftUI 修饰符,写在闭包外
.background(Color.red) // SwiftUI 修饰符,写在闭包外
}
FontSize 和 FontWeight
FontSize 和 FontWeight 是 MarkdownUI 在 markdownTextStyle 闭包中使用的专属 DSL,不是 SwiftUI 原生类型。
1、FontSize
支持两种写法:
FontSize(16) // 固定像素值
FontSize(.em(0.85)) // 相对于父级字号的倍数,0.85 表示缩小为 85%
2、FontWeight
与 SwiftUI 的 Font.Weight 完全一致,常用值:
FontWeight(.ultraLight)
FontWeight(.light)
FontWeight(.regular)
FontWeight(.medium)
FontWeight(.semibold)
FontWeight(.bold)
FontWeight(.heavy)
FontWeight(.black)
其他可用属性
在 markdownTextStyle 闭包中还可以使用:
FontFamily(.custom("Georgia")) // 自定义字体
FontFamilyVariant(.monospaced) // 等宽变体,适合 inlineCode
FontStyle(.italic) // 斜体
ForegroundColor(.orange) // 文字颜色
BackgroundColor(.yellow.opacity(0.3)) // 文字背景色(高亮效果)
StrikethroughStyle(.single) // 删除线
UnderlineStyle(.single) // 下划线
常用操作
1、从本地加载Markdown文件
struct LessonView: View {
@State private var content: String = ""
let lessonFile: String // 例如: "01-basics/01-variables"
var body: some View {
ScrollView {
if !content.isEmpty {
Markdown(content)
.markdownTheme(.gitHub)
.markdownCodeSyntaxHighlighter(.splash(theme: .sunset(withFont: .init(size: 14))))
.padding()
} else {
ProgressView("加载中...")
}
}
.onAppear {
loadMarkdown()
}
}
func loadMarkdown() {
// 方法 1: 从 Bundle 加载
if let path = Bundle.main.path(forResource: lessonFile, ofType: "md"),
let markdown = try? String(contentsOfFile: path) {
content = markdown
}
// 方法 2: 从 Bundle URL 加载
if let url = Bundle.main.url(forResource: lessonFile, withExtension: "md"),
let markdown = try? String(contentsOf: url) {
content = markdown
}
}
}
2、显示本地图片
在 Markdown 文件中,图片路径可以是相对路径:

在代码中添加 .markdownImageProvider(.asset) 修饰符,MarkdownUI 会自动提取路径中最后的文件名,从 Assets 中加载对应图片,无需手动修改 Markdown 文件中的路径:
// Swift 代码
Markdown(content)
.markdownImageProvider(.asset) // 从 Assets 加载本地图片
