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

Swift中NSAttributedString无序列表字体异常及替代方案咨询

问题:HTML转NSAttributedString时无序列表字体不生效,求替代方案

我最近在把HTML字符串转换成NSAttributedString时遇到了个麻烦:给body设置样式后,普通文本的字体能正常改成我指定的-apple-system、Avenir-Medium这些,但只要HTML里包含<ul>和<li>的无序列表,列表项的字体就完全不受样式影响,哪怕我单独给ul、li加样式也没用。

我成功修改普通文本的代码是这样的:

let myOriginalString: String = "<!DOCTYPE html> <head> <style type=\"text/css\"> body{font-family: '-apple-system','Avenir-Medium','Avenir-Black'; font-size:14;} </style> <body> My HTML content </body>"
let data: Data = myOriginalString.data(using: .utf8)!
let myConvertedString: NSAttributedString = try! NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)

但换成带列表的HTML就不行了:

let myOriginalString: String = "<!DOCTYPE html> <head> <style type=\"text/css\"> body{font-family: '-apple-system','Avenir-Medium','Avenir-Black'; font-size:14;} </style> <body> My HTML content <ul> <li>Item 1</li> <li>Item 2</li> </ul> </body>"
let data: Data = myOriginalString.data(using: .utf8)!
let myConvertedString: NSAttributedString = try! NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)

想请教下:为什么只有无序列表的字体没法正常渲染?有没有不依赖HTML来创建带无序列表的NSAttributedString的更好方法?


为什么列表项字体不生效?

这其实是iOS的NSAttributedString解析HTML时的一个小坑——当解析<ul>和<li>标签时,系统会给列表项默认应用系统自带的列表字体样式,这个样式的优先级会比你在head里设置的body样式更高。哪怕你直接给li加font-family,也会被系统的默认样式覆盖。

本质上是因为NSAttributedString解析HTML依赖的WebKit引擎,对列表元素有内置的样式规则,这些规则会优先于用户自定义的body级样式。

不依赖HTML创建无序列表NSAttributedString的更优方式

完全可以直接通过NSMutableAttributedString来构建带无序列表的富文本,不需要绕HTML的弯路,灵活性也更高。这里给你两种常用的方法:

方法1:手动添加列表符号+缩进

这种方法适合简单的列表,直接给每个列表项添加项目符号(比如●),再设置段落缩进:

func createBulletedList(items: [String], font: UIFont) -> NSAttributedString {
    let mutableAttrString = NSMutableAttributedString()
    
    let paragraphStyle = NSMutableParagraphStyle()
    // 设置缩进:左边留足够空间放符号,首行缩进抵消符号的宽度
    paragraphStyle.headIndent = 20
    paragraphStyle.firstLineHeadIndent = -20
    
    let attributes: [NSAttributedString.Key: Any] = [
        .font: font,
        .paragraphStyle: paragraphStyle
    ]
    
    for item in items {
        // 先加符号,再加列表文本
        let bulletPoint = "● "
        let bulletAttrString = NSAttributedString(string: bulletPoint, attributes: attributes)
        let itemAttrString = NSAttributedString(string: "\(item)\n", attributes: attributes)
        
        mutableAttrString.append(bulletAttrString)
        mutableAttrString.append(itemAttrString)
    }
    
    // 移除最后多余的换行
    if mutableAttrString.length > 0 {
        mutableAttrString.deleteCharacters(in: NSRange(location: mutableAttrString.length - 1, length: 1))
    }
    
    return mutableAttrString
}

// 使用示例
let listFont = UIFont(name: "Avenir-Medium", size: 14)!
let listAttrString = createBulletedList(items: ["Item 1", "Item 2"], font: listFont)

方法2:使用NSParagraphStyle的listStyle(iOS 11+)

iOS 11及以后,系统提供了更原生的列表样式支持,直接通过NSParagraphStyle的listStyle属性来设置,不需要手动加符号:

func createNativeBulletedList(items: [String], font: UIFont) -> NSAttributedString {
    let mutableAttrString = NSMutableAttributedString()
    
    let paragraphStyle = NSMutableParagraphStyle()
    // 设置为无序列表样式
    paragraphStyle.listStyle = .disc
    // 可调整列表缩进距离
    paragraphStyle.listIndent = 16
    
    let attributes: [NSAttributedString.Key: Any] = [
        .font: font,
        .paragraphStyle: paragraphStyle
    ]
    
    for (index, item) in items.enumerated() {
        let itemString = index == items.count - 1 ? item : "\(item)\n"
        let itemAttrString = NSAttributedString(string: itemString, attributes: attributes)
        mutableAttrString.append(itemAttrString)
    }
    
    return mutableAttrString
}

// 使用示例
let listFont = UIFont(name: "Avenir-Medium", size: 14)!
let nativeListAttrString = createNativeBulletedList(items: ["Item 1", "Item 2"], font: listFont)

这种方法的好处是系统会自动处理列表符号的样式、对齐和缩进,比手动添加符号更规范,也更适配不同系统版本的样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:36:59