问题描述
我想要一个具有不同段落样式的字符串。目标是为字符串的不同部分自定义段落/行间距。我研究并发现了这个 answer 但由于我添加了多个换行符,不确定如何实现。
设计
这是我在布局方面的目标:
代码
这是我的代码,使它看起来像上面的左图。请参阅代码中的注释 Not working
。注意主字符串的间距是如何设置的,但其他字符串不能设置它们自己的自定义间距:
struct BookModel: Codable {
let main: String
let detail: String
}
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainString = NSMutableAttributedString(string: model.main)
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.linespacing = 10
mainStringParagraphStyle.paragraphSpacing = 30
let mainStringAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: mainStringParagraphStyle]
let spacingAfterQuote = NSMutableAttributedString(string: "\n")
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
let lineParagraphStyle = NSMutableParagraphStyle()
lineParagraphStyle.alignment = .left
lineParagraphStyle.linespacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
lineParagraphStyle.paragraphSpacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
let lineAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: lineParagraphStyle]
let spacingAfterSeparator = NSMutableAttributedString(string: "\n")
let spacingAfterSeparatorParagraphStyle = NSMutableParagraphStyle()
spacingAfterSeparatorParagraphStyle.alignment = .left
spacingAfterSeparatorParagraphStyle.linespacing = 0 // Not working - instead of 0 it is 30 from `mainStringParagraphStyle`
spacingAfterSeparatorParagraphStyle.paragraphSpacing = 5 // Not working - instead of 5 it is 30 from `mainStringParagraphStyle`
let spacingAfterSeparatorAttributes: [NSAttributedString.Key: Any] = [.paragraphStyle: spacingAfterSeparatorParagraphStyle]
let detailString = NSMutableAttributedString(string: model.detail)
let detailStringAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20)]
fullString.append(mainString)
fullString.append(spacingAfterQuote)
fullString.append(lineImageString)
fullString.append(spacingAfterSeparator)
fullString.append(detailString)
fullString.addAttributes(mainStringAttributes,range: fullString.mutableString.range(of: model.main))
fullString.addAttributes(lineAttributes,range: fullString.mutableString.range(of: lineImageString.string))
fullString.addAttributes(spacingAfterSeparatorAttributes,range: fullString.mutableString.range(of: spacingAfterSeparator.string))
fullString.addAttributes(detailStringAttributes,range: fullString.mutableString.range(of: model.detail))
return fullString
}
对如何实现右侧的图像有任何想法吗?
问题更新 1
下面的代码有效!只有一个小问题。当我添加 linespacing
时,主字符串的最后一行末尾有额外的空格。请注意,我将此设置为零:mainStringParagraphStyle.paragraphSpacing = 0
,但末尾仍有空格,因为 mainStringParagraphStyle.linespacing = 60
。
我问这个的原因是为了对间距进行更细粒度的控制。例如,在线条图像和主字符串之间有一个完美的数字。对此有什么想法吗?
代码:
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 0 // The space after the end of the paragraph
mainStringParagraphStyle.linespacing = 60 // NOTE: This controls the spacing after the last line instead of just `paragraphSpacing`
let mainString = NSAttributedString(string: "\(model.main)\n",attributes: [.paragraphStyle: mainStringParagraphStyle,.font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-view"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle,value: lineImageStringParagraphStyle,range: NSRange(location: 0,length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
detailStringParagraphStyle.linespacing = 0
let detailString = NSAttributedString(string: "\n\(model.detail)",attributes: [.paragraphStyle: detailStringParagraphStyle,.font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
解决方法
更新答案:
这是一个新的例子。我用图像在段落的顶部和底部设置了间距。如果需要,这允许在 model.main
和 model.detail
中使用换行符。此外,我使用了 lineSpacing
而不是 lineHeightMultiple
。这个参数影响行之间的缩进而不影响最后一行:
func createAttributedString(for model: BookModel) -> NSAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.lineHeightMultiple = 2 // Note that this is a multiplier,not a value in points
let mainString = NSAttributedString(string: "\(model.main)\n",attributes: [.paragraphStyle: mainStringParagraphStyle,.font: UIFont.systemFont(ofSize: 24)])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
lineImageStringParagraphStyle.paragraphSpacingBefore = 10 // The space before image
lineImageStringParagraphStyle.paragraphSpacing = 20 // The space after image
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle,value: lineImageStringParagraphStyle,range: NSRange(location: 0,length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
let detailString = NSAttributedString(string: "\n\(model.detail)",attributes: [.paragraphStyle: detailStringParagraphStyle,.font: UIFont.systemFont(ofSize: 12)])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}
也看看我的图书馆StringEx。它允许您从模板创建 NSAttributedString
并应用样式,而无需编写大量代码:
import StringEx
...
func createAttributedString(for model: BookModel) -> NSAttributedString {
let pattern = "<main />\n<image />\n<detail />"
let ex = pattern.ex
ex[.tag("main")]
.insert(model.main)
.style([
.aligment(.center),.lineHeightMultiple(2),.font(.systemFont(ofSize: 24))
])
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSAttributedString(attachment: lineImageAttachment)
ex[.tag("image")]
.insert(lineImageString)
.style([
.aligment(.center),.paragraphSpacingBefore(10),.paragraphSpacing(20)
])
ex[.tag("detail")]
.insert(model.detail)
.style([
.aligment(.center),.font(.systemFont(ofSize: 12))
])
return ex.attributedString
}
旧答案:
我觉得你可以只设置第一段末尾的间距(主字符串)和最后一段开头的间距(细节字符串):
func createAttributedString(for model: BookModel) -> NSMutableAttributedString {
let fullString = NSMutableAttributedString()
let mainStringParagraphStyle = NSMutableParagraphStyle()
mainStringParagraphStyle.alignment = .center
mainStringParagraphStyle.paragraphSpacing = 30 // The space after the end of the paragraph
let mainString = NSAttributedString(string: "\(model.main)\n",attributes: [.paragraphStyle: mainStringParagraphStyle])
let lineImageStringParagraphStyle = NSMutableParagraphStyle()
lineImageStringParagraphStyle.alignment = .center
let lineImageAttachment = NSTextAttachment(image: #imageLiteral(resourceName: "line-image"))
let lineImageString = NSMutableAttributedString(attachment: lineImageAttachment)
lineImageString.addAttribute(.paragraphStyle,length: lineImageString.length))
let detailStringParagraphStyle = NSMutableParagraphStyle()
detailStringParagraphStyle.alignment = .center
detailStringParagraphStyle.paragraphSpacingBefore = 5 // The distance between the paragraph’s top and the beginning of its text content
let detailString = NSAttributedString(string: "\n\(model.detail)",attributes: [.paragraphStyle: detailStringParagraphStyle])
fullString.append(mainString)
fullString.append(lineImageString)
fullString.append(detailString)
return fullString
}