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
相关产品推荐
相关产品推荐

