NSAttributedString 和 html 样式(子弹对齐)
NSAttributedString and html styling (bullet alignment)
在我的 iOS 应用程序中,我使用 NSAttributedString 生成项目符号列表。不幸的是,我正在努力让子弹看起来像样。我的第一次尝试是为项目符号使用常规文本和 unicode 字符,基本上使用如下字符串:
var attributedString = NSMutableAttributedString(
string: "Here is a list of bullets and a paragraph introducing them, note that this paragraph spans multiple lines\n" +
"• This is the first bullet\n" +
"• Here is a second bullet\n" +
"• And here is a third bullet with a lot of text such that it overflows to the next line"
)
结果是这样的:
我喜欢项目符号的外观,但最后一个项目符号中溢出的文本应该与之前的行对齐,我不知道如何用纯文本实现这一点(没有对段落应用相同的对齐方式)以上)。
我的第二次尝试是通过 NSHTMLTextDocumentType 在 NSAttributedString 中使用 html,并使用 <ul>
和 <li>
元素生成项目符号。
let content = "Here is a list of bullets and a paragraph introducing them, note that this paragraph spans multiple lines" +
"<ul>" +
"<li>This is the first bullet</li>" +
"<li>Here is a second bullet</li>" +
"<li>And here is a third bullet with a lot of text such that it overflows to the next line</li>" +
"</ul>"
var attributedString = try! NSMutableAttributedString(
data: content,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil
)
解决了第一个问题,但引入了一个新问题:
项目符号现在间隔太远(从左边缘到右边的文本)。我尝试使用典型的 html/css 技巧来修复对齐方式 (<li style="text-indent: -10px;">
),但这些样式似乎被 NSAttributedString 忽略了。
我试图通过额外的 NSMutableParagraphStyle 来解决这个问题,但它似乎弊大于利。这是我尝试过的:
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = 20
attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: attributedStringRange)
这是我得到的:
如您所见,它只会让事情变得更糟,这是我的问题:
- 它确实偏移了第二行,但我只想要项目符号的效果,而不是前面的段落(我想我可以通过减少应用效果的范围来补救)
- 我必须 guess/hardcode 偏移量,在我的示例中我选择了 20,这对于给定当前字体设置的项目符号来说是不够的,这可能会改变
- 出于某种原因,项目符号间距现在无缘无故地拉长了,似乎只是应用了一个普通的 NSParagraphStyle 而没有做任何事情,我看不到解决这个问题的方法。
我真正想要的是让我的项目符号间距看起来类似于第一个屏幕截图,而第二行的溢出缩进看起来像第二行,而无需硬编码确切的像素位置。你们能帮帮我吗?
谢谢
我必须向列表添加自定义样式,这是我最终为 NSAttributedString
中的每个项目符号使用的段落样式
headIndent
和 firstLineHeadIndent
可以更改,但 NSTextTab
位置应与 headIndent
相同
NSMutableParagraphStyle *const bulletParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
bulletParagraphStyle.headIndent = 60;
bulletParagraphStyle.firstLineHeadIndent = 30;
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural location:60 options:@{}];
bulletParagraphStyle.tabStops = @[listTab];
假设你的要点后面有一个 \t
(然后是文本)
使用 <table>
而不是 <ul><li>
来处理项目符号对齐。
ul{margin:0;padding:0}
或 li{margin-left: 0;}
等样式将被忽略。
示例:
let html = "<table>" +
"<tr><td valign=\"top\" style=\"padding-right:16px\">•</td><td valign=\"top\">text 1</td></tr>" +
"<tr><td valign=\"top\" style=\"padding-right:16px\">•</td><td valign=\"top\">text 2</td></tr>" +
"</table>"
let attributedString = try! NSAttributedString(data: html.data(using: String.Encoding.utf8)!,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue,],
documentAttributes: nil)
这是我这样做的替代方法:
attributedString.enumerateAttribute(.paragraphStyle,
in: NSRange(location: 0, length: attributedString.length),
options: [])
{ (paragraphStyle, range, _) in
guard let paragraphStyle = paragraphStyle as? NSParagraphStyle else { return }
let updatedStyle = NSMutableParagraphStyle()
updatedStyle.setParagraphStyle(paragraphStyle)
// Here we can modify paragraphs. Also could add condition to update only paragraphs with lists.
updatedStyle.firstLineHeadIndent = 0
updatedStyle.headIndent = 20
attributedString.addAttribute(.paragraphStyle, value: updatedStyle, range: range)
}
主要想法不是制作一个段落(据我所知是刹车)来改变现有的段落。为了实现,我使用 enumerateAttribute(...)
给我所有现有的段落及其范围。这个问题让我想到了这个想法:Get the range of a paragraph NSAttributedString
在我的 iOS 应用程序中,我使用 NSAttributedString 生成项目符号列表。不幸的是,我正在努力让子弹看起来像样。我的第一次尝试是为项目符号使用常规文本和 unicode 字符,基本上使用如下字符串:
var attributedString = NSMutableAttributedString(
string: "Here is a list of bullets and a paragraph introducing them, note that this paragraph spans multiple lines\n" +
"• This is the first bullet\n" +
"• Here is a second bullet\n" +
"• And here is a third bullet with a lot of text such that it overflows to the next line"
)
结果是这样的:
我喜欢项目符号的外观,但最后一个项目符号中溢出的文本应该与之前的行对齐,我不知道如何用纯文本实现这一点(没有对段落应用相同的对齐方式)以上)。
我的第二次尝试是通过 NSHTMLTextDocumentType 在 NSAttributedString 中使用 html,并使用 <ul>
和 <li>
元素生成项目符号。
let content = "Here is a list of bullets and a paragraph introducing them, note that this paragraph spans multiple lines" +
"<ul>" +
"<li>This is the first bullet</li>" +
"<li>Here is a second bullet</li>" +
"<li>And here is a third bullet with a lot of text such that it overflows to the next line</li>" +
"</ul>"
var attributedString = try! NSMutableAttributedString(
data: content,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil
)
解决了第一个问题,但引入了一个新问题:
项目符号现在间隔太远(从左边缘到右边的文本)。我尝试使用典型的 html/css 技巧来修复对齐方式 (<li style="text-indent: -10px;">
),但这些样式似乎被 NSAttributedString 忽略了。
我试图通过额外的 NSMutableParagraphStyle 来解决这个问题,但它似乎弊大于利。这是我尝试过的:
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = 20
attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: attributedStringRange)
这是我得到的:
如您所见,它只会让事情变得更糟,这是我的问题:
- 它确实偏移了第二行,但我只想要项目符号的效果,而不是前面的段落(我想我可以通过减少应用效果的范围来补救)
- 我必须 guess/hardcode 偏移量,在我的示例中我选择了 20,这对于给定当前字体设置的项目符号来说是不够的,这可能会改变
- 出于某种原因,项目符号间距现在无缘无故地拉长了,似乎只是应用了一个普通的 NSParagraphStyle 而没有做任何事情,我看不到解决这个问题的方法。
我真正想要的是让我的项目符号间距看起来类似于第一个屏幕截图,而第二行的溢出缩进看起来像第二行,而无需硬编码确切的像素位置。你们能帮帮我吗?
谢谢
我必须向列表添加自定义样式,这是我最终为 NSAttributedString
headIndent
和 firstLineHeadIndent
可以更改,但 NSTextTab
位置应与 headIndent
NSMutableParagraphStyle *const bulletParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
bulletParagraphStyle.headIndent = 60;
bulletParagraphStyle.firstLineHeadIndent = 30;
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentNatural location:60 options:@{}];
bulletParagraphStyle.tabStops = @[listTab];
假设你的要点后面有一个 \t
(然后是文本)
使用 <table>
而不是 <ul><li>
来处理项目符号对齐。
ul{margin:0;padding:0}
或 li{margin-left: 0;}
等样式将被忽略。
示例:
let html = "<table>" +
"<tr><td valign=\"top\" style=\"padding-right:16px\">•</td><td valign=\"top\">text 1</td></tr>" +
"<tr><td valign=\"top\" style=\"padding-right:16px\">•</td><td valign=\"top\">text 2</td></tr>" +
"</table>"
let attributedString = try! NSAttributedString(data: html.data(using: String.Encoding.utf8)!,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue,],
documentAttributes: nil)
这是我这样做的替代方法:
attributedString.enumerateAttribute(.paragraphStyle,
in: NSRange(location: 0, length: attributedString.length),
options: [])
{ (paragraphStyle, range, _) in
guard let paragraphStyle = paragraphStyle as? NSParagraphStyle else { return }
let updatedStyle = NSMutableParagraphStyle()
updatedStyle.setParagraphStyle(paragraphStyle)
// Here we can modify paragraphs. Also could add condition to update only paragraphs with lists.
updatedStyle.firstLineHeadIndent = 0
updatedStyle.headIndent = 20
attributedString.addAttribute(.paragraphStyle, value: updatedStyle, range: range)
}
主要想法不是制作一个段落(据我所知是刹车)来改变现有的段落。为了实现,我使用 enumerateAttribute(...)
给我所有现有的段落及其范围。这个问题让我想到了这个想法:Get the range of a paragraph NSAttributedString