You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何用SwiftUI实现类似备忘录的富文本编辑器?

SwiftUI 富文本编辑器实现方案

一、SwiftUI 标准TextEditor的局限

标准的TextEditor仅支持纯文本编辑,你提到的文本样式(标题、副标题等)、格式(粗体/斜体等)、颜色、列表、对齐、块引用这些富文本功能,SwiftUI目前没有内置支持,不存在你忽略的现有API。

二、实现富文本编辑器的可行方案

你需要基于现有组件构建或借助成熟方案,以下是两种主流思路:

1. 包装UIKit的UITextView(推荐)

UITextView原生支持NSAttributedString,可以轻松实现所有你需要的富文本功能,通过SwiftUI的UIViewRepresentable可以将其包装成SwiftUI可用的视图。核心步骤:

  • 创建UIViewRepresentable结构体,将UITextView嵌入SwiftUI视图层级
  • NSAttributedString替代原有的String存储文本内容
  • 实现格式切换逻辑:通过操作选中范围的属性(如字体、颜色、段落样式)来应用粗体、斜体、列表等效果
  • 处理文本编辑回调,同步富文本数据到你的SwiftData模型(可将NSAttributedString转成Data存储)

示例代码思路(替换原TextEditor):

import SwiftUI
import UIKit
import SwiftData

struct RichTextEditor: UIViewRepresentable {
    @Binding var attributedText: NSAttributedString
    var onTextChange: (NSAttributedString) -> Void
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.delegate = context.coordinator
        textView.font = UIFont.systemFont(ofSize: 16)
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        if uiView.attributedText != attributedText {
            uiView.attributedText = attributedText
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextViewDelegate {
        var parent: RichTextEditor
        
        init(_ parent: RichTextEditor) {
            self.parent = parent
        }
        
        func textViewDidChange(_ textView: UITextView) {
            parent.onTextChange(textView.attributedText)
        }
    }
}

// 在NoteView中使用
struct NoteView: View {
    let note: Note
    @Environment(\.modelContext) private var context
    @State private var attributedText: NSAttributedString = NSAttributedString(string: "")
    
    var body: some View {
        VStack {
            // 格式工具栏示例:添加粗体、项目符号等按钮
            HStack {
                Button("加粗") {
                    applyBold()
                }
                Button("项目符号") {
                    applyBulletList()
                }
                // 可扩展其他格式按钮:斜体、下划线、对齐等
            }
            .padding(.horizontal)
            
            RichTextEditor(attributedText: $attributedText) { newText in
                attributedText = newText
                // 同步到SwiftData:将NSAttributedString转成Data存储
                if let data = try? NSKeyedArchiver.archivedData(withRootObject: newText, requiringSecureCoding: false) {
                    note.bodyData = data
                }
            }
            .padding()
        }
        .onAppear {
            // 从SwiftData加载富文本数据
            if let data = note.bodyData,
               let text = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSAttributedString.self, from: data) {
                attributedText = text
            } else {
                attributedText = NSAttributedString(string: note.body ?? "")
            }
        }
    }
    
    // 示例:实现加粗切换功能
    private func applyBold() {
        guard let textView = UIApplication.shared.sendAction(#selector(getter: UITextView.self), to: nil, from: nil, for: nil) as? UITextView,
              let selectedRange = textView.selectedTextRange else { return }
        
        let range = textView.selectedRange
        var attributes = textView.attributedText.attributes(at: range.location, effectiveRange: nil)
        
        if let font = attributes[.font] as? UIFont {
            let newFontDescriptor = font.fontDescriptor.symbolicTraits.contains(.traitBold) ?
                font.fontDescriptor.withSymbolicTraits([]) :
                font.fontDescriptor.withSymbolicTraits(.traitBold)
            attributes[.font] = UIFont(descriptor: newFontDescriptor ?? font.fontDescriptor, size: font.pointSize)
            textView.textStorage.addAttributes(attributes, range: range)
        }
    }
    
    // 示例:实现项目符号列表
    private func applyBulletList() {
        guard let textView = UIApplication.shared.sendAction(#selector(getter: UITextView.self), to: nil, from: nil, for: nil) as? UITextView,
              let selectedRange = textView.selectedTextRange else { return }
        
        let range = textView.selectedRange
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.headIndent = 15
        paragraphStyle.firstLineHeadIndent = -15
        paragraphStyle.minimumLineHeight = 20
        paragraphStyle.listStyle = .disc
        
        textView.textStorage.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
    }
}

// 更新Note模型以支持富文本存储
@Model
class Note {
    var body: String?
    var bodyData: Data? // 存储富文本的二进制数据
    
    init(body: String? = nil) {
        self.body = body
    }
}

2. 使用第三方富文本库

有一些专为SwiftUI打造的富文本编辑器库,已封装好UITextView的交互和格式逻辑,可直接集成使用,比如:

  • RichTextEditor:支持基础格式、列表、对齐等功能,API贴合SwiftUI风格
  • AttributedTextEditor:基于NSAttributedString构建,提供灵活的样式自定义能力

这类库能帮你省去大量底层封装工作,直接专注于业务逻辑。

三、参考资源

  • 苹果官方文档:NSAttributedString属性设置、UITextView代理方法、UIViewRepresentable使用指南
  • 苹果WWDC教程:UIKit与SwiftUI交互、富文本编辑最佳实践相关内容

内容的提问来源于stack exchange,提问作者milkycards

火山引擎 最新活动