You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UITextView中NSHTMLTextDocumentType解析<ul>列表项目符号重复问题

解决UITextView中HTML列表项目符号重复的问题

这个坑我之前踩过!当你用NSHTMLTextDocumentType把HTML转换成NSAttributedString并在UITextView显示时,<ul>列表的项目符号重复,通常是两种原因导致的:

  • 你的HTML代码里,<li>标签内部手动添加了项目符号(比如•、-这类字符),而iOS的HTML解析器会自动给<ul>的<li>生成系统样式的项目符号,两者叠加就出现了重复。
  • 解析后的富文本段落样式和UITextView的默认列表样式发生了冲突,导致符号重复渲染。

下面给你两种针对性的解决办法:

方法一:预处理HTML,移除手动添加的符号

如果是HTML本身带了重复符号,最直接的方式是先清理HTML内容,去掉<li>开头的手动符号,让系统自动生成统一的列表符号:

// 假设你的原始HTML存在contentHTML变量中
// 替换掉<li>开头的手动符号(这里以•为例,你可以根据实际情况修改匹配规则)
let cleanedHTML = contentHTML.replacingOccurrences(of: "<li>• ", with: "<li>")

guard let data = cleanedHTML.data(using: .utf8) else { return }

// 注意:现在推荐用NSAttributedString的新枚举类型,避免旧的String键值
let attributedOptions: [NSAttributedString.DocumentReadingOptionKey: Any] = [
    .documentType: NSAttributedString.DocumentType.html,
    .characterEncoding: String.Encoding.utf8.rawValue
]

do {
    let attributedString = try NSAttributedString(data: data, options: attributedOptions, documentAttributes: nil)
    yourTextView.attributedText = attributedString
} catch {
    print("HTML解析出错:\(error.localizedDescription)")
}

如果你的HTML里的符号格式不固定,还可以用正则表达式来匹配替换,比如匹配<li>后面任意符号加空格的情况:

let regex = try! NSRegularExpression(pattern: "<li>[•\\-\\*] ", options: .caseInsensitive)
let cleanedHTML = regex.stringByReplacingMatches(in: contentHTML, range: NSRange(contentHTML.startIndex..., in: contentHTML), withTemplate: "<li>")

方法二:修改解析后的富文本段落样式

如果HTML本身没有手动符号,是解析时的样式冲突导致的重复,那可以直接修改生成的NSAttributedString的段落属性,移除重复的列表样式:

guard let data = contentHTML.data(using: .utf8) else { return }

let attributedOptions: [NSAttributedString.DocumentReadingOptionKey: Any] = [
    .documentType: NSAttributedString.DocumentType.html,
    .characterEncoding: String.Encoding.utf8.rawValue
]

do {
    let attributedString = try NSAttributedString(data: data, options: attributedOptions, documentAttributes: nil)
    let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
    
    // 遍历所有段落,检查并调整列表样式
    let fullRange = NSRange(location: 0, length: mutableAttributedString.length)
    mutableAttributedString.enumerateAttribute(.paragraphStyle, in: fullRange, options: []) { (value, range, _) in
        guard let paragraphStyle = value as? NSMutableParagraphStyle else { return }
        
        // 判断当前段落是否是列表项(通过缩进和列表样式判断)
        if paragraphStyle.headIndent > 0, paragraphStyle.listStyle == .disc {
            // 移除系统自动添加的列表样式,避免和HTML里的符号重复
            paragraphStyle.listStyle = nil
            // 调整缩进,保证文本排版美观
            paragraphStyle.headIndent = 16
            paragraphStyle.firstLineHeadIndent = 16
            mutableAttributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
        }
    }
    
    yourTextView.attributedText = mutableAttributedString
} catch {
    print("HTML解析出错:\(error.localizedDescription)")
}

额外注意点

  • iOS的HTML解析依赖WebKit,不同版本的系统可能有细微的样式差异,测试时尽量覆盖多版本。
  • 如果HTML是后端返回的,优先和后端沟通,让他们输出标准的<ul><li>结构,不要手动添加项目符号,从源头避免问题。
  • 如果你需要自定义列表符号的样式(比如颜色、大小),可以在修改段落样式时,结合NSParagraphStyle的listItemPrefix属性来实现。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:15:06