UITextView中用NSParagraphStyle实现多级项目符号列表对齐异常问题
问题分析与解决方案
核心问题原因
- 字符宽度差异:不同Unicode项目符号(如•\u{2022}、○\u{25E6}、△\u{2023})在当前字体下的渲染宽度不一致,你用固定15点的制表位无法适配这种差异,导致文本起始位置错位。
- 制表位位置理解错误:你的第二个假设不成立——
NSTextTab的location是相对于段落左边界,而非firstLineHeadIndent的缩进后位置。这意味着你设置的制表位是距离屏幕左边缘15点的固定位置,叠加层级缩进后,整个排版逻辑完全混乱。
修正后的实现方案
步骤1:计算项目符号的实际宽度
先根据当前文本字体,算出项目符号的渲染宽度,以此动态调整文本起始位置,替代固定制表位的依赖。
步骤2:重构段落样式生成逻辑
去掉固定制表位配置,通过firstLineHeadIndent和headIndent配合符号宽度实现对齐,同时保留层级缩进:
func makeBulletStyle(forLevel indentLevel: Int, bulletCharacter: String, font: UIFont) -> NSParagraphStyle { let indentInterval: CGFloat = 15 let bulletSpacing: CGFloat = 4 // 项目符号与文本的间距 // 计算当前字体下项目符号的实际宽度 let bulletSize = bulletCharacter.size(withAttributes: [.font: font]) let bulletTotalWidth = bulletSize.width + bulletSpacing let result = NSMutableParagraphStyle() // 层级缩进:每级向右缩进indentInterval距离 let levelIndent = indentInterval * CGFloat(indentLevel) result.firstLineHeadIndent = levelIndent // 换行后的文本对齐到「层级缩进 + 符号总宽度」的位置 result.headIndent = levelIndent + bulletTotalWidth return result }
步骤3:调整富文本构建方式
不再给文本前缀加•\t,直接拼接项目符号和文本,配合上述段落样式即可:
// 示例:构建层级为1的富文本列表项 let bulletChar = "\u{25E6}" // 空心圆符号 let contentText = "这是列表项的具体内容,换行后会自动对齐到符号右侧" let textFont = UIFont.systemFont(ofSize: 16) // 生成对应层级的段落样式 let paragraphStyle = makeBulletStyle(forLevel: 1, bulletCharacter: bulletChar, font: textFont) // 拼接符号与文本,设置富文本属性 let fullText = "\(bulletChar) \(contentText)" let attributes: [NSAttributedString.Key: Any] = [ .font: textFont, .paragraphStyle: paragraphStyle ] textView.attributedText = NSAttributedString(string: fullText, attributes: attributes)
关于indent interval的作用
你设置的indentInterval是列表层级缩进的步长,每提升一个层级,整个段落就向右缩进指定距离。之前不设置时文本跑到符号下一行,是因为headIndent未正确配置,导致换行后的文本没有对齐到项目符号右侧的位置。
内容的提问来源于stack exchange,提问作者Tom Hamming
相关产品推荐
相关产品推荐

